From 9e81a0fa9230e8d3cbbcd108e149174535c59c04 Mon Sep 17 00:00:00 2001 From: raichoo Date: Wed, 5 Feb 2020 10:02:33 +0000 Subject: [PATCH] Let there be light! --- .boring | 125 ++ .clang-format | 126 ++ LICENSE | 9 + Makefile | 129 ++ README.md | 125 ++ doc/example_hikari.conf | 275 ++++ hikari_unlocker.c | 88 ++ include/hikari/background.h | 20 + include/hikari/border.h | 40 + include/hikari/color.h | 15 + include/hikari/command.h | 7 + include/hikari/completion.h | 34 + include/hikari/configuration.h | 72 + include/hikari/exec.h | 45 + include/hikari/exec_select_mode.h | 15 + include/hikari/font.h | 20 + include/hikari/geometry.h | 30 + include/hikari/grab_keyboard_mode.h | 10 + include/hikari/group.h | 43 + include/hikari/group_assign_mode.h | 20 + include/hikari/indicator.h | 83 ++ include/hikari/indicator_bar.h | 42 + include/hikari/indicator_frame.h | 25 + include/hikari/input_buffer.h | 36 + include/hikari/keybinding.h | 18 + include/hikari/keyboard.h | 25 + include/hikari/keyboard_grab_mode.h | 14 + include/hikari/layout.h | 27 + include/hikari/mark.h | 54 + include/hikari/mark_assign_mode.h | 21 + include/hikari/mark_select_mode.h | 17 + include/hikari/maximized_state.h | 38 + include/hikari/memory.h | 15 + include/hikari/mode.h | 36 + include/hikari/move_mode.h | 15 + include/hikari/normal_mode.h | 15 + include/hikari/operation.h | 27 + include/hikari/output.h | 60 + include/hikari/pointer_config.h | 20 + include/hikari/render_data.h | 18 + include/hikari/resize_mode.h | 15 + include/hikari/server.h | 294 +++++ include/hikari/sheet.h | 92 ++ include/hikari/split.h | 82 ++ include/hikari/tile.h | 32 + include/hikari/tiling_mode.h | 13 + include/hikari/unlocker.h | 18 + include/hikari/utf8.h | 42 + include/hikari/view.h | 323 +++++ include/hikari/view_autoconf.h | 30 + include/hikari/view_interface.h | 46 + include/hikari/workspace.h | 142 ++ include/hikari/xdg_view.h | 43 + include/hikari/xwayland_unmanaged_view.h | 39 + include/hikari/xwayland_view.h | 28 + main.c | 16 + src/background.c | 28 + src/border.c | 151 +++ src/command.c | 32 + src/completion.c | 83 ++ src/configuration.c | 1427 ++++++++++++++++++++ src/exec.c | 26 + src/exec_select_mode.c | 230 ++++ src/font.c | 37 + src/geometry.c | 55 + src/group.c | 61 + src/group_assign_mode.c | 412 ++++++ src/indicator.c | 156 +++ src/indicator_bar.c | 154 +++ src/indicator_frame.c | 113 ++ src/input_buffer.c | 81 ++ src/keybinding.c | 14 + src/keyboard.c | 118 ++ src/keyboard_grab_mode.c | 110 ++ src/layout.c | 47 + src/mark.c | 58 + src/mark_assign_mode.c | 432 ++++++ src/mark_select_mode.c | 250 ++++ src/maximized_state.c | 24 + src/memory.c | 21 + src/move_mode.c | 93 ++ src/normal_mode.c | 238 ++++ src/output.c | 412 ++++++ src/pointer_config.c | 9 + src/resize_mode.c | 100 ++ src/server.c | 1033 +++++++++++++++ src/sheet.c | 415 ++++++ src/split.c | 219 ++++ src/tile.c | 54 + src/tiling_mode.c | 104 ++ src/unlocker.c | 153 +++ src/view.c | 1527 ++++++++++++++++++++++ src/view_autoconf.c | 15 + src/workspace.c | 1064 +++++++++++++++ src/xdg_view.c | 533 ++++++++ src/xwayland_unmanaged_view.c | 207 +++ src/xwayland_view.c | 417 ++++++ 97 files changed, 13822 insertions(+) create mode 100644 .boring create mode 100644 .clang-format create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 doc/example_hikari.conf create mode 100644 hikari_unlocker.c create mode 100644 include/hikari/background.h create mode 100644 include/hikari/border.h create mode 100644 include/hikari/color.h create mode 100644 include/hikari/command.h create mode 100644 include/hikari/completion.h create mode 100644 include/hikari/configuration.h create mode 100644 include/hikari/exec.h create mode 100644 include/hikari/exec_select_mode.h create mode 100644 include/hikari/font.h create mode 100644 include/hikari/geometry.h create mode 100644 include/hikari/grab_keyboard_mode.h create mode 100644 include/hikari/group.h create mode 100644 include/hikari/group_assign_mode.h create mode 100644 include/hikari/indicator.h create mode 100644 include/hikari/indicator_bar.h create mode 100644 include/hikari/indicator_frame.h create mode 100644 include/hikari/input_buffer.h create mode 100644 include/hikari/keybinding.h create mode 100644 include/hikari/keyboard.h create mode 100644 include/hikari/keyboard_grab_mode.h create mode 100644 include/hikari/layout.h create mode 100644 include/hikari/mark.h create mode 100644 include/hikari/mark_assign_mode.h create mode 100644 include/hikari/mark_select_mode.h create mode 100644 include/hikari/maximized_state.h create mode 100644 include/hikari/memory.h create mode 100644 include/hikari/mode.h create mode 100644 include/hikari/move_mode.h create mode 100644 include/hikari/normal_mode.h create mode 100644 include/hikari/operation.h create mode 100644 include/hikari/output.h create mode 100644 include/hikari/pointer_config.h create mode 100644 include/hikari/render_data.h create mode 100644 include/hikari/resize_mode.h create mode 100644 include/hikari/server.h create mode 100644 include/hikari/sheet.h create mode 100644 include/hikari/split.h create mode 100644 include/hikari/tile.h create mode 100644 include/hikari/tiling_mode.h create mode 100644 include/hikari/unlocker.h create mode 100644 include/hikari/utf8.h create mode 100644 include/hikari/view.h create mode 100644 include/hikari/view_autoconf.h create mode 100644 include/hikari/view_interface.h create mode 100644 include/hikari/workspace.h create mode 100644 include/hikari/xdg_view.h create mode 100644 include/hikari/xwayland_unmanaged_view.h create mode 100644 include/hikari/xwayland_view.h create mode 100644 main.c create mode 100644 src/background.c create mode 100644 src/border.c create mode 100644 src/command.c create mode 100644 src/completion.c create mode 100644 src/configuration.c create mode 100644 src/exec.c create mode 100644 src/exec_select_mode.c create mode 100644 src/font.c create mode 100644 src/geometry.c create mode 100644 src/group.c create mode 100644 src/group_assign_mode.c create mode 100644 src/indicator.c create mode 100644 src/indicator_bar.c create mode 100644 src/indicator_frame.c create mode 100644 src/input_buffer.c create mode 100644 src/keybinding.c create mode 100644 src/keyboard.c create mode 100644 src/keyboard_grab_mode.c create mode 100644 src/layout.c create mode 100644 src/mark.c create mode 100644 src/mark_assign_mode.c create mode 100644 src/mark_select_mode.c create mode 100644 src/maximized_state.c create mode 100644 src/memory.c create mode 100644 src/move_mode.c create mode 100644 src/normal_mode.c create mode 100644 src/output.c create mode 100644 src/pointer_config.c create mode 100644 src/resize_mode.c create mode 100644 src/server.c create mode 100644 src/sheet.c create mode 100644 src/split.c create mode 100644 src/tile.c create mode 100644 src/tiling_mode.c create mode 100644 src/unlocker.c create mode 100644 src/view.c create mode 100644 src/view_autoconf.c create mode 100644 src/workspace.c create mode 100644 src/xdg_view.c create mode 100644 src/xwayland_unmanaged_view.c create mode 100644 src/xwayland_view.c diff --git a/.boring b/.boring new file mode 100644 index 0000000..1b6e64c --- /dev/null +++ b/.boring @@ -0,0 +1,125 @@ +# This file contains a list of extended regular expressions, one per +# line. A file path matching any of these expressions will be filtered +# out during `darcs add`, or when the `--look-for-adds` flag is passed +# to `darcs whatsnew` and `record`. The entries in ~/.darcs/boring (if +# it exists) supplement those in this file. +# +# Blank lines, and lines beginning with an octothorpe (#) are ignored. +# See regex(7) for a description of extended regular expressions. + +### compiler and interpreter intermediate files +# haskell (ghc) interfaces +\.hi$ +\.hi-boot$ +\.o-boot$ +# object files +\.o$ +\.o\.cmd$ +# profiling haskell +\.p_hi$ +\.p_o$ +# haskell program coverage resp. profiling info +\.tix$ +\.prof$ +# fortran module files +\.mod$ +# linux kernel +\.ko\.cmd$ +\.mod\.c$ +(^|/)\.tmp_versions($|/) +# *.ko files aren't boring by default because they might +# be Korean translations rather than kernel modules +# \.ko$ +# python, emacs, java byte code +\.py[co]$ +\.elc$ +\.class$ +# objects and libraries; lo and la are libtool things +\.(obj|a|exe|so|lo|la)$ +# compiled zsh configuration files +\.zwc$ +# Common LISP output files for CLISP and CMUCL +\.(fas|fasl|sparcf|x86f)$ + +### build and packaging systems +# cabal intermediates +\.installed-pkg-config +\.setup-config +# standard cabal build dir, might not be boring for everybody +# ^dist(/|$) +# autotools +(^|/)autom4te\.cache($|/) +(^|/)config\.(log|status)$ +# microsoft web expression, visual studio metadata directories +\_vti_cnf$ +\_vti_pvt$ +# gentoo tools +\.revdep-rebuild.* +# generated dependencies +^\.depend$ + +### version control systems +# cvs +(^|/)CVS($|/) +\.cvsignore$ +# cvs, emacs locks +^\.# +# rcs +(^|/)RCS($|/) +,v$ +# subversion +(^|/)\.svn($|/) +# mercurial +(^|/)\.hg($|/) +# git +(^|/)\.git($|/) +# bzr +\.bzr$ +# sccs +(^|/)SCCS($|/) +# darcs +(^|/)_darcs($|/) +(^|/)\.darcsrepo($|/) +# gnu arch +(^|/)(\+|,) +(^|/)vssver\.scc$ +\.swp$ +(^|/)MT($|/) +(^|/)\{arch\}($|/) +(^|/).arch-ids($|/) +# bitkeeper +(^|/)BitKeeper($|/) +(^|/)ChangeSet($|/) + +### miscellaneous +# backup files +~$ +\.bak$ +\.BAK$ +# patch originals and rejects +\.orig$ +\.rej$ +# X server +\..serverauth.* +# image spam +\# +(^|/)Thumbs\.db$ +# vi, emacs tags +(^|/)(tags|TAGS)$ +#(^|/)\.[^/] +# core dumps +(^|/|\.)core$ +# partial broken files (KIO copy operations) +\.part$ +# waf files, see http://code.google.com/p/waf/ +(^|/)\.waf-[[:digit:].]+-[[:digit:]]+($|/) +(^|/)\.lock-wscript$ +# mac os finder +(^|/)\.DS_Store$ +# emacs saved sessions (desktops) +(^|.*/)\.emacs\.desktop(\.lock)?$ + # stack +(^|/)\.stack-work($|/) +^hikari$ +^hikari-unlocker$ +^xdg-shell-protocol.h$ diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..52e0c5c --- /dev/null +++ b/.clang-format @@ -0,0 +1,126 @@ +--- +Language: Cpp +# BasedOnStyle: Mozilla +AccessModifierOffset: -2 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowAllArgumentsOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: TopLevel +AlwaysBreakAfterReturnType: TopLevel +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: false + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeComma +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - wl_list_for_each + - wl_list_for_each_reverse + - wl_list_for_each_safe + - wl_list_for_each_reverse_safe + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: false +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +... + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..81ac153 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +Copyright 2020 raichoo + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4d62200 --- /dev/null +++ b/Makefile @@ -0,0 +1,129 @@ +OBJS = \ + background.o \ + border.o \ + command.o \ + completion.o \ + configuration.o \ + exec.o \ + exec_select_mode.o \ + font.o \ + geometry.o \ + group.o \ + group_assign_mode.o \ + indicator.o \ + indicator_bar.o \ + indicator_frame.o \ + input_buffer.o \ + keybinding.o \ + keyboard.o \ + keyboard_grab_mode.o \ + layout.o \ + main.o \ + mark.o \ + mark_assign_mode.o \ + mark_select_mode.o \ + maximized_state.o \ + memory.o \ + move_mode.o \ + normal_mode.o \ + output.o \ + pointer_config.o \ + resize_mode.o \ + server.o \ + sheet.o \ + split.o \ + tile.o \ + tiling_mode.o \ + unlocker.o \ + view.o \ + view_autoconf.o \ + workspace.o \ + xdg_view.o \ + xwayland_unmanaged_view.o \ + xwayland_view.o + +WAYLAND_PROTOCOLS = /usr/local/share/wayland-protocols + +.PHONY: clean debug +.PATH: src + +.ifmake debug +CFLAGS = -g -O0 -fsanitize=address +.else +CFLAGS += -DNDEBUG +.endif + +CFLAGS += -Wall -I. -Iinclude + +WLROOTS_CFLAGS ?= `pkg-config --cflags wlroots` +WLROOTS_LIBS ?= `pkg-config --libs wlroots` + +WLROOTS_CFLAGS += -DWLR_USE_UNSTABLE=1 + +EPOLLSHIM_LIBS ?= `pkg-config --libs epoll-shim` + +PANGO_CFLAGS ?= `pkg-config --cflags pangocairo` +PANGO_LIBS ?= `pkg-config --libs pangocairo` + +CAIRO_CFLAGS ?= `pkg-config --cflags cairo` +CAIRO_LIBS ?= `pkg-config --libs cairo` + +GLIB_CFLAGS ?= `pkg-config --cflags glib-2.0` +GLIB_LIBS ?= `pkg-config --libs glib-2.0` + +PIXMAN_CFLAGS ?= `pkg-config --cflags pixman-1` +PIXMAN_LIBS ?= `pkg-config --libs pixman-1` + +XKBCOMMON_CFLAGS ?= `pkg-config --cflags xkbcommon` +XKBCOMMON_LIBS ?= `pkg-config --libs xkbcommon` + +WAYLAND_CFLAGS ?= `pkg-config --cflags wayland-server` +WAYLAND_LIBS ?= `pkg-config --libs wayland-server` + +LIBINPUT_CFLAGS ?= `pkg-config --cflags libinput` +LIBINPUT_LIBS ?= `pkg-config --libs libinput` + +UCL_CFLAGS ?= `pkg-config --cflags libucl` +UCL_LIBS ?= `pkg-config --libs libucl` + +CFLAGS += \ + ${WLROOTS_CFLAGS} \ + ${PANGO_CFLAGS} \ + ${CAIRO_CFLAGS} \ + ${GLIB_CFLAGS} \ + ${PIXMAN_CFLAGS} \ + ${XKBCOMMON_CFLAGS} \ + ${WAYLAND_CFLAGS} \ + ${LIBINPUT_CFLAGS} \ + ${UCL_CFLAGS} + +LIBS = \ + ${WLROOTS_LIBS} \ + ${EPOLLSHIM_LIBS} \ + ${PANGO_LIBS} \ + ${CAIRO_LIBS} \ + ${GLIB_LIBS} \ + ${PIXMAN_LIBS} \ + ${XKBCOMMON_LIBS} \ + ${WAYLAND_LIBS} \ + ${LIBINPUT_LIBS} \ + ${UCL_LIBS} + +all: hikari hikari-unlocker + +hikari: xdg-shell-protocol.h ${OBJS} + ${CC} ${LDFLAGS} ${CFLAGS} ${INCLUDES} ${LIBS} ${OBJS} -o ${.TARGET} + +xdg-shell-protocol.h: + wayland-scanner server-header ${WAYLAND_PROTOCOLS}/stable/xdg-shell/xdg-shell.xml ${.TARGET} + +hikari-unlocker: hikari_unlocker.c + ${CC} -lpam hikari_unlocker.c -o hikari-unlocker + +clean: + rm *.o ||: + rm hikari ||: + rm hikari-unlocker ||: + rm xdg-shell-protocol.h ||: + +debug: hikari diff --git a/README.md b/README.md new file mode 100644 index 0000000..32d6876 --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ +# Hikari - Wayland Compositor + +![Screenshot](https://acmelabs.space/~raichoo/hikari.png) + +## Description + +_hikari_ is a stacking Wayland compositor with additional tiling capabilities, +it is heavily inspired by the Calm Window manager (cwm(1)). Its core concepts +are *views*, *groups*, *sheets* and the *workspace*. + +The workspace is the set of views that are currently visible. + +A sheet is a collection of views, each view can only be a member of a single +sheet. Switching between sheets will replace the current content of the +workspace with all the views that are a member of the selected sheet. _hikari_ +has 9 general purpose sheets that correspond to the numbers **1** to **9** and a +special purpose sheet **0**. Views that are a member of sheet **0** will +always be visible but stacked below the views of the selected sheet. + +Groups are a bit more fine grained than sheets. Like sheets, groups are a +collection of views. Unlike sheets you can have a arbitrary number of groups +and each group can have an arbitrary name. Views from one group can be spread +among all available sheets. Some operations act on entire groups rather than +individual views. + +Please note that `hikari` is currently in `alpha` state and Wayland still +requires some work on FreeBSD. This release is targeted towards people who want +to help improving `hikari` by either providing feedback, patches +and/or help improving Wayland on FreeBSD. + +## Setting up Wayland on FreeBSD + +Wayland currently requires some care to work properly on FreeBSD. This section +aims to document the recent state of how to enable Wayland on the FreeBSD +`STABLE` branch and will change once support is being improved. + +### Local `devfs.rules` + +NOTE: this should only be used on single user setups since it allows members of +the `video` group to gain access to the `input` devices. This is a temporary +solution until FreeBSD offers ways to handle this in a better way. + +Add the following to `/etc/devfs.rules` + +``` +[localrules=10] +add path 'input/*' mode 0660 group video +``` + +Add `devfs_system_ruleset="localrules"` to `/etc/rc.conf`. + +### Start `moused` + +Some systems might require `moused` for mice to work. Enable it with `service +moused enable` + +### Setting up XDG\_RUNTIME\_DIR + +This section describes how to use `/tmp` as your `XDG_RUNTIME_DIR`. Some Wayland +clients (e.g. native Wayland `firefox`) require `posix_fallocate` to work in +that directory. This is not supported by ZFS, therefore you should prevent the +ZFS tmp dataset from mounting to `/tmp` and `mount -t tmpfs tmpfs /tmp`. To +persist this setting edit your `/etc/fstab` appropriately to automatically mount +`tmpfs` during boot. + +Additionally set `XDG_RUNTIME_DIR` to `/tmp` in your environment. + +### Setting up XKB + +`hikari` currently gets its `xkb` settings settings the appropriate environment +variables to something like the following. + +``` +XKB_DEFAULT_RULES "evdev" +XKB_DEFAULT_LAYOUT "de(nodeadkeys),de" +``` + +## Building + +`hikari` currently only works on FreeBSD. This will likely change in the future. + +### Dependencies + +* wlroots +* pango +* cairo +* libinput +* xkbcommon +* pixman +* libucl +* glib +* epoll-shim + +### Compiling and Installing + +To build all executables required to run `hikari` simply run `make`, this will +produce two binaries `hikari` and `hikari-unlocker`. The latter one is used to +check credentials for unlocking the screen. Both need to be installed with +root setuid in your `PATH`. + +`hikari` can be configured via `$HOME/.config/hikari/hikari.conf`, an example +can be found under `doc/example_hikari.conf`. + +## TODO before 1.0 + +* Keybindings currently only work with `libinput` keycodes. +* Popups are not positioned correctly on multi monitor setups. +* Configuration processing is still buggy and can lead to issues when errors are + encountered. +* Manpage is still missing. + +## Community + +The `hikari` community gears to be inclusive and welcoming to everyone, this is +why we chose to adere to the [Geekfeminism Code of +Conduct](https://geekfeminismdotorg.wordpress.com/about/code-of-conduct/). + +If you care to be a part of our community, please join our Matrix chat at +`#hikari:acmelabs.space` and/or subscribe to our mailing list by sending a mail +to `hikari+subscribe@acmelabs.space`. + +## Contributing + +Please make sure you use `clang-format` with the accompanying `.clag-format` +configuration before submitting any patches. diff --git a/doc/example_hikari.conf b/doc/example_hikari.conf new file mode 100644 index 0000000..1707f24 --- /dev/null +++ b/doc/example_hikari.conf @@ -0,0 +1,275 @@ +colorscheme { + background = 0x282C34 + foreground = 0x000000 + indicator_selected = 0xE6DB74 + indicator_grouped = 0xFD971F + indicator_first = 0xB8E673 + indicator_conflict = 0xEF5939 + indicator_insert = 0x66D9EF + border_active = 0xFFFFFF + border_inactive = 0x465457 +} + +border = 1 +gap = 5 + +inputs { + pointers { + "System mouse" = { + accel = 1 + scroll-method = on-button-down + scroll-button = 274 + } + } +} + +backgrounds { + eDP-1 = ".wallpaper" +} + +font = "DejaVu Sans Mono 10" + +execute { + c = "sakura --name cmus -c 150 -r 50 -e /usr/local/bin/cmus" + f = "/usr/local/bin/firefox" + m = "sakura -t mutt --name mutt -c 125 -r 37 -e /usr/local/bin/mutt" + i = "sakura --name irssi -r 65 -c 227 -e irssi" + t = "sakura -t top --name top -e /usr/bin/top -I -P -t -s5" + s = "sakura -t systat --name systat -e /usr/bin/systat -vm 5" +} + +autoconf { + shell = { + group = shell + } + + rootshell = { + group = rootshell + mark = r + } + + jabber = { + group = communication + sheet = 1 + mark = j + position = { + x = 30 + y = 20 + } + focus = true + } + + irssi = { + group = communication + sheet = 1 + mark = i + position = { + x = 940 + y = 20 + } + focus = true + } + + mutt = { + group = communication + sheet = 1 + mark = m + position = { + x = 30 + y = 860 + } + focus = true + } + + cmus = { + group = music + mark = c + } + + top = { + group = monitor + sheet = 0 + position = { + x = 1997 # 2560 - 1 - 562 + y = 1077 # 1440 - 1 - 362 + } + } + + systat = { + group = monitor + sheet = 0 + position = { + x = 1429 # 2560 - 2 - 2 * 562 - 5 + y = 1077 # 1440 - 1 - 362 + } + focus = true + } + + firefox = { + group = web + sheet = 2 + mark = f + } +} + +layouts { + stack = { + vertical { + ratio = 0.25 + left = { + container { + views = 1 + type = full + } + } + right = { + container { + type = horizontal + } + } + } + } + + grid = { + container { + type = grid + } + } + + vertical = { + container { + type = vertical + } + } + + horizontal = { + container { + type = horizontal + } + } + + full = { + container { + type = full + } + } +} + +actions { + shell = "sakura --name shell" + rootshell = "sakura --name rootshell -e su - root -c fish" + volup = "mixer -s vol +5" + voldown = "mixer -s vol -5" +} + +bindings { + keyboard { + L-11 = sheet-switch-to-0 + L-2 = sheet-switch-to-1 + L-3 = sheet-switch-to-2 + L-4 = sheet-switch-to-3 + L-5 = sheet-switch-to-4 + L-6 = sheet-switch-to-5 + L-7 = sheet-switch-to-6 + L-8 = sheet-switch-to-7 + L-9 = sheet-switch-to-8 + L-10 = sheet-switch-to-9 + L-43 = sheet-switch-to-alternate # # + L-52 = sheet-switch-to-current # . + L-36 = sheet-switch-to-next # j + L-37 = sheet-switch-to-prev # k + L-51 = sheet-switch-to-next-inhabited + LS-51 = sheet-switch-to-prev-inhabited + + LC-23 = sheet-show-iconified + LC-31 = sheet-show-all + L-31 = sheet-cycle-view-next + LS-31 = sheet-cycle-view-prev + LA-19 = sheet-reset-layout + LA-102 = sheet-cycle-layout-view-first # Pos1 + LA-107 = sheet-cycle-layout-view-last # Ende + L-38 = sheet-cycle-layout-view-next + LS-38 = sheet-cycle-layout-view-prev + L-45 = sheet-exchange-layout-view-next + LS-45 = sheet-exchange-layout-view-prev + LA-45 = sheet-exchange-layout-view-main + + LS-11 = view-pin-to-sheet-0 + LS-2 = view-pin-to-sheet-1 + LS-3 = view-pin-to-sheet-2 + LS-4 = view-pin-to-sheet-3 + LS-5 = view-pin-to-sheet-4 + LS-6 = view-pin-to-sheet-5 + LS-7 = view-pin-to-sheet-6 + LS-8 = view-pin-to-sheet-7 + LS-9 = view-pin-to-sheet-8 + LS-10 = view-pin-to-sheet-9 + LS-43 = view-pin-to-sheet-alternate + LS-52 = view-pin-to-sheet-current # . + LS-36 = view-pin-to-sheet-next # j + LS-37 = view-pin-to-sheet-prev # k + + L-22 = view-raise # u + L-32 = view-lower # d + L-24 = view-only + L-35 = view-hide + L-16 = view-quit + + L-103 = view-move-up + L-108 = view-move-down + L-105 = view-move-left + L-106 = view-move-right + LA-103 = view-decrease-size-up + LA-108 = view-increase-size-down + LA-105 = view-decrease-size-left + LA-106 = view-increase-size-right + LS-103 = view-snap-up + LS-108 = view-snap-down + LS-105 = view-snap-left + LS-106 = view-snap-right + L-19 = view-reset-geometry + + L-53 = view-toggle-maximize-vertical + L-86 = view-toggle-maximize-horizontal + L-33 = view-toggle-maximize-full + L5-27 = view-toggle-floating + L-23 = view-toggle-iconified + + LS-24 = group-only + LS-35 = group-hide + LS-22 = group-raise # u + LS-32 = group-lower # d + L-15 = group-cycle-next + LS-15 = group-cycle-prev + L-41 = group-cycle-view-next # grave + LS-41 = group-cycle-view-prev + L-102 = group-cycle-view-first # Pos1 + L-107 = group-cycle-view-last # Ende + + L-18 = mode-enter-execute + L-34 = mode-enter-group-assign + L-50 = mode-enter-mark-assign + L-13 = mode-enter-mark-select + LS-13 = mode-enter-mark-switch-select + LCA-34 = mode-enter-keyboard-grab + + LS-14 = lock + LCA-16 = quit + + L-28 = action-shell + LS-28 = action-rootshell + 0-115 = action-volup + 0-114 = action-voldown + + LA-31 = layout-stack + LA-47 = layout-vertical + LA-35 = layout-horizontal + LA-34 = layout-grid + LA-33 = layout-full + } + + mouse { + L-272 = mode-enter-move + L-273 = mode-enter-resize + } +} diff --git a/hikari_unlocker.c b/hikari_unlocker.c new file mode 100644 index 0000000..93faddb --- /dev/null +++ b/hikari_unlocker.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *input_buffer = NULL; +const int INPUT_BUFFER_SIZE = 1024; + +static int +conversation_handler(int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *data) +{ + struct pam_response *pam_reply = calloc(num_msg, sizeof(struct pam_response)); + + if (pam_reply == NULL) { + return PAM_ABORT; + } + *resp = pam_reply; + for (int i = 0; i < num_msg; ++i) { + switch (msg[i]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + pam_reply[i].resp = strdup(input_buffer); + if (pam_reply[i].resp == NULL) { + return PAM_ABORT; + } + break; + + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + break; + } + } + return PAM_SUCCESS; +} + +bool +check_password(void) +{ + struct passwd *passwd = getpwuid(getuid()); + char *username = passwd->pw_name; + + const struct pam_conv conv = { + .conv = conversation_handler, + .appdata_ptr = NULL, + }; + + bool success = false; + pam_handle_t *auth_handle = NULL; + if (pam_start("passwd", username, &conv, &auth_handle) != PAM_SUCCESS) { + return false; + } + + read(0, input_buffer, INPUT_BUFFER_SIZE - 1); + int pam_status = pam_authenticate(auth_handle, 0); + bzero(input_buffer, INPUT_BUFFER_SIZE); + success = pam_status == PAM_SUCCESS; + write(1, &success, sizeof(bool)); + + pam_end(auth_handle, pam_status); + + return success; +} + +int +main(int argc, char **argv) +{ + char input; + bool success = false; + input_buffer = malloc(INPUT_BUFFER_SIZE); + mlock(input_buffer, INPUT_BUFFER_SIZE); + + while (!success) { + success = check_password(); + } + + munlock(input_buffer, INPUT_BUFFER_SIZE); + free(input_buffer); + + return 0; +} diff --git a/include/hikari/background.h b/include/hikari/background.h new file mode 100644 index 0000000..c61df86 --- /dev/null +++ b/include/hikari/background.h @@ -0,0 +1,20 @@ +#if !defined(HIKARI_BACKGROUND_H) +#define HIKARI_BACKGROUND_H + +#include + +struct hikari_background { + struct wl_list link; + + char *output_name; + char *path; +}; + +void +hikari_background_init( + struct hikari_background *background, const char *output_name, char *path); + +void +hikari_background_fini(struct hikari_background *background); + +#endif diff --git a/include/hikari/border.h b/include/hikari/border.h new file mode 100644 index 0000000..3f41d68 --- /dev/null +++ b/include/hikari/border.h @@ -0,0 +1,40 @@ +#if !defined(HIKARI_BORDER_H) +#define HIKARI_BORDER_H + +#include + +#include + +struct hikari_render_data; + +enum hikari_border_state { + HIKARI_BORDER_NONE, + HIKARI_BORDER_INACTIVE, + HIKARI_BORDER_ACTIVE +}; + +struct hikari_border { + enum hikari_border_state state; + + struct wlr_box geometry; + struct wlr_box top; + struct wlr_box bottom; + struct wlr_box left; + struct wlr_box right; +}; + +static inline struct wlr_box * +hikari_border_geometry(struct hikari_border *border) +{ + return &border->geometry; +} + +void +hikari_border_render( + struct hikari_border *border, struct hikari_render_data *render_data); + +void +hikari_border_refresh_geometry( + struct hikari_border *border, struct wlr_box *geometry); + +#endif diff --git a/include/hikari/color.h b/include/hikari/color.h new file mode 100644 index 0000000..37fd297 --- /dev/null +++ b/include/hikari/color.h @@ -0,0 +1,15 @@ +#if !defined(HIKARI_COLOR_H) +#define HIKARI_COLOR_H + +#include + +static inline void +hikari_color_convert(float dst[static 4], uint32_t color) +{ + dst[0] = ((color >> 16) & 0xff) / 255.0; + dst[1] = ((color >> 8) & 0xff) / 255.0; + dst[2] = (color & 0xff) / 255.0; + dst[3] = 1.0; +} + +#endif diff --git a/include/hikari/command.h b/include/hikari/command.h new file mode 100644 index 0000000..5554f89 --- /dev/null +++ b/include/hikari/command.h @@ -0,0 +1,7 @@ +#if !defined(HIKARI_COMMAND_H) +#define HIKARI_COMMAND_H + +void +hikari_command_execute(const char *cmd); + +#endif diff --git a/include/hikari/completion.h b/include/hikari/completion.h new file mode 100644 index 0000000..8802ba6 --- /dev/null +++ b/include/hikari/completion.h @@ -0,0 +1,34 @@ +#if !defined(HIKARI_COMPLETION_H) +#define HIKARI_COMPLETION_H + +#include + +struct hikari_completion_item { + char data[256]; + struct wl_list completion_items; +}; + +struct hikari_completion { + struct hikari_completion_item *current_item; + struct wl_list items; +}; + +void +hikari_completion_init(struct hikari_completion *completion, char *data); + +void +hikari_completion_fini(struct hikari_completion *completion); + +void +hikari_completion_add(struct hikari_completion *completion, char *data); + +char * +hikari_completion_cancel(struct hikari_completion *completion); + +char * +hikari_completion_next(struct hikari_completion *completion); + +char * +hikari_completion_prev(struct hikari_completion *completion); + +#endif diff --git a/include/hikari/configuration.h b/include/hikari/configuration.h new file mode 100644 index 0000000..41888c0 --- /dev/null +++ b/include/hikari/configuration.h @@ -0,0 +1,72 @@ +#if !defined(HIKARI_CONFIGURATION_H) +#define HIKARI_CONFIGURATION_H + +#include +#include +#include + +#include + +struct hikari_group; +struct hikari_keybinding; +struct hikari_sheet; +struct hikari_view; +struct hikari_pointer_config; + +struct hikari_configuration { + float clear[4]; + float foreground[4]; + float indicator_selected[4]; + float indicator_grouped[4]; + float indicator_first[4]; + float indicator_conflict[4]; + float indicator_insert[4]; + float border_active[4]; + float border_inactive[4]; + + struct hikari_font font; + + int border; + int gap; + + struct { + uint8_t nkeybindings[256]; + struct hikari_keybinding *keybindings[256]; + + uint8_t nmousebindings[256]; + struct hikari_keybinding *mousebindings[256]; + } normal_mode; + + struct wl_list autoconfs; + struct wl_list backgrounds; + struct wl_list pointer_configs; +}; + +extern struct hikari_configuration hikari_configuration; + +void +hikari_configuration_init(struct hikari_configuration *configuration); + +void +hikari_configuration_fini(struct hikari_configuration *configuration); + +void +hikari_configuration_resolve_view_autoconf( + struct hikari_configuration *configuration, + const char *app_id, + struct hikari_view *view, + struct hikari_sheet **sheet, + struct hikari_group **group, + int *x, + int *y, + bool *focus); + +char * +hikari_configuration_resolve_background( + struct hikari_configuration *configuration, const char *output_name); + +struct hikari_pointer_config * +hikari_configuration_resolve_pointer_config( + struct hikari_configuration *configuration, const char *pointer_name); + +#endif diff --git a/include/hikari/exec.h b/include/hikari/exec.h new file mode 100644 index 0000000..460f653 --- /dev/null +++ b/include/hikari/exec.h @@ -0,0 +1,45 @@ +#if !defined(HIKARI_EXEC_H) +#define HIKARI_EXEC_H + +static const int HIKARI_NR_OF_EXECS = 26; + +struct hikari_exec { + char *command; +}; + +extern struct hikari_exec execs[HIKARI_NR_OF_EXECS]; + +static struct hikari_exec *HIKARI_EXEC_a = &execs[0]; +static struct hikari_exec *HIKARI_EXEC_b = &execs[1]; +static struct hikari_exec *HIKARI_EXEC_c = &execs[2]; +static struct hikari_exec *HIKARI_EXEC_d = &execs[3]; +static struct hikari_exec *HIKARI_EXEC_e = &execs[4]; +static struct hikari_exec *HIKARI_EXEC_f = &execs[5]; +static struct hikari_exec *HIKARI_EXEC_g = &execs[6]; +static struct hikari_exec *HIKARI_EXEC_h = &execs[7]; +static struct hikari_exec *HIKARI_EXEC_i = &execs[8]; +static struct hikari_exec *HIKARI_EXEC_j = &execs[9]; +static struct hikari_exec *HIKARI_EXEC_k = &execs[10]; +static struct hikari_exec *HIKARI_EXEC_l = &execs[11]; +static struct hikari_exec *HIKARI_EXEC_m = &execs[12]; +static struct hikari_exec *HIKARI_EXEC_n = &execs[13]; +static struct hikari_exec *HIKARI_EXEC_o = &execs[14]; +static struct hikari_exec *HIKARI_EXEC_p = &execs[15]; +static struct hikari_exec *HIKARI_EXEC_q = &execs[16]; +static struct hikari_exec *HIKARI_EXEC_r = &execs[17]; +static struct hikari_exec *HIKARI_EXEC_s = &execs[18]; +static struct hikari_exec *HIKARI_EXEC_t = &execs[19]; +static struct hikari_exec *HIKARI_EXEC_u = &execs[20]; +static struct hikari_exec *HIKARI_EXEC_v = &execs[21]; +static struct hikari_exec *HIKARI_EXEC_w = &execs[22]; +static struct hikari_exec *HIKARI_EXEC_x = &execs[23]; +static struct hikari_exec *HIKARI_EXEC_y = &execs[24]; +static struct hikari_exec *HIKARI_EXEC_z = &execs[25]; + +void +hikari_execs_init(void); + +void +hikari_execs_fini(void); + +#endif diff --git a/include/hikari/exec_select_mode.h b/include/hikari/exec_select_mode.h new file mode 100644 index 0000000..d89d81d --- /dev/null +++ b/include/hikari/exec_select_mode.h @@ -0,0 +1,15 @@ +#if !defined(HIKARI_EXEC_SELECT_MODE_H) +#define HIKARI_EXEC_SELECT_MODE_H + +#include + +#include + +struct hikari_exec_select_mode { + struct hikari_mode mode; +}; + +void +hikari_exec_select_mode_init(struct hikari_exec_select_mode *exec_select_mode); + +#endif diff --git a/include/hikari/font.h b/include/hikari/font.h new file mode 100644 index 0000000..ad59440 --- /dev/null +++ b/include/hikari/font.h @@ -0,0 +1,20 @@ +#if !defined(HIKARI_FONT_H) +#define HIKARI_FONT_H + +#include + +struct hikari_font { + PangoFontDescription *desc; +}; + +void +hikari_font_init(struct hikari_font *font, const char *font_name); + +void +hikari_font_fini(struct hikari_font *font); + +void +hikari_font_metrics( + struct hikari_font *font, const char *text, int *width, int *height); + +#endif diff --git a/include/hikari/geometry.h b/include/hikari/geometry.h new file mode 100644 index 0000000..9ea79c2 --- /dev/null +++ b/include/hikari/geometry.h @@ -0,0 +1,30 @@ +#if !defined(HIKARI_GEOMETRY_H) +#define HIKARI_GEOMETRY_H + +#include + +void +hikari_geometry_split_vertical(struct wlr_box *src, + float factor, + int gap, + struct wlr_box *left, + struct wlr_box *right); + +void +hikari_geometry_split_horizontal(struct wlr_box *src, + float factor, + int gap, + struct wlr_box *top, + struct wlr_box *bottom); + +void +hikari_geometry_shrink(struct wlr_box *geometry, int gap); + +void +hikari_geometry_constrain_position(struct wlr_box *geometry, + int screen_width, + int screen_height, + int x, + int y); + +#endif diff --git a/include/hikari/grab_keyboard_mode.h b/include/hikari/grab_keyboard_mode.h new file mode 100644 index 0000000..e22cade --- /dev/null +++ b/include/hikari/grab_keyboard_mode.h @@ -0,0 +1,10 @@ +#if !defined(HIKARI_KEYBOARD_GRAB_MODE_H) +#define HIKARI_KEYBOARD_GRAB_MODE_H + +#include + +struct hikari_keyboard_grab_mode { + struct hikari_mode mode; +}; + +#endif diff --git a/include/hikari/group.h b/include/hikari/group.h new file mode 100644 index 0000000..a1ebfb2 --- /dev/null +++ b/include/hikari/group.h @@ -0,0 +1,43 @@ +#if !defined(HIKARIGROUP_H) +#define HIKARIGROUP_H + +#include + +#include + +struct hikari_sheet; +struct hikari_view; +struct hikari_workspace; + +struct hikari_group { + char *name; + struct hikari_sheet *sheet; + + struct wl_list views; + struct wl_list visible_views; + + struct wl_list server_groups; + struct wl_list visible_server_groups; +}; + +void +hikari_group_init(struct hikari_group *group, const char *name); + +void +hikari_group_fini(struct hikari_group *group); + +struct hikari_view * +hikari_group_first_view( + struct hikari_group *group, struct hikari_workspace *workspace); + +struct hikari_view * +hikari_group_last_view( + struct hikari_group *group, struct hikari_workspace *workspace); + +static inline bool +hikari_group_is_sheet(struct hikari_group *group) +{ + return group->sheet != NULL; +} + +#endif diff --git a/include/hikari/group_assign_mode.h b/include/hikari/group_assign_mode.h new file mode 100644 index 0000000..87b5231 --- /dev/null +++ b/include/hikari/group_assign_mode.h @@ -0,0 +1,20 @@ +#if !defined(HIKARI_GROUP_ASSIGN_MODE_H) +#define HIKARI_GROUP_ASSIGN_MODE_H + +#include +#include + +struct hikari_group; + +struct hikari_group_assign_mode { + struct hikari_mode mode; + struct hikari_input_buffer input_buffer; + struct hikari_completion *completion; + struct hikari_group *group; +}; + +void +hikari_group_assign_mode_init( + struct hikari_group_assign_mode *group_assign_mode); + +#endif diff --git a/include/hikari/indicator.h b/include/hikari/indicator.h new file mode 100644 index 0000000..764d6a9 --- /dev/null +++ b/include/hikari/indicator.h @@ -0,0 +1,83 @@ +#if !defined(HIKARI_INDICATOR_H) +#define HIKARI_INDICATOR_H + +#include +#include +#include + +struct hikari_render_data; +struct hikari_view; +struct hikari_output; + +struct hikari_indicator { + struct hikari_indicator_bar title; + struct hikari_indicator_bar sheet; + struct hikari_indicator_bar group; + struct hikari_indicator_bar mark; + + int bar_height; +}; + +void +hikari_indicator_init( + struct hikari_indicator *indicator, float color[static 4]); + +void +hikari_indicator_fini(struct hikari_indicator *indicator); + +void +hikari_indicator_render( + struct hikari_indicator *indicator, struct hikari_render_data *render_data); + +void +hikari_indicator_update(struct hikari_indicator *indicator, + struct hikari_view *view, + float background[static 4]); + +void +hikari_indicator_update_sheet(struct hikari_indicator *indicator, + struct wlr_box *view_geometry, + struct hikari_output *output, + struct hikari_sheet *sheet, + float background[static 4], + bool iconified, + bool floating); + +void +hikari_indicator_damage( + struct hikari_indicator *indicator, struct hikari_view *view); + +static inline void +hikari_indicator_update_title(struct hikari_indicator *indicator, + struct wlr_box *view_geometry, + struct hikari_output *output, + const char *text, + float background[static 4]) +{ + hikari_indicator_bar_update( + &indicator->title, view_geometry, output, text, background); +} + +static inline void +hikari_indicator_update_group(struct hikari_indicator *indicator, + struct wlr_box *view_geometry, + struct hikari_output *output, + const char *text, + float background[static 4]) +{ + hikari_indicator_bar_update( + &indicator->group, view_geometry, output, text, background); +} + +static inline void +hikari_indicator_update_mark(struct hikari_indicator *indicator, + struct wlr_box *view_geometry, + struct hikari_output *output, + const char *text, + float background[static 4]) +{ + hikari_indicator_bar_update( + &indicator->mark, view_geometry, output, text, background); +} + +#endif diff --git a/include/hikari/indicator_bar.h b/include/hikari/indicator_bar.h new file mode 100644 index 0000000..ce75a72 --- /dev/null +++ b/include/hikari/indicator_bar.h @@ -0,0 +1,42 @@ +#if !defined(HIKARI_INDICATOR_BAR_H) +#define HIKARI_INDICATOR_BAR_H + +#include + +struct hikari_indicator; +struct hikari_render_data; +struct hikari_output; + +struct hikari_indicator_bar { + struct wlr_texture *texture; + struct hikari_indicator *indicator; + + int width; + int offset; +}; + +void +hikari_indicator_bar_init(struct hikari_indicator_bar *indicator_bar, + struct hikari_indicator *indicator, + int offset); + +void +hikari_indicator_bar_fini(struct hikari_indicator_bar *indicator_bar); + +void +hikari_indicator_bar_update(struct hikari_indicator_bar *indicator_bar, + struct wlr_box *view_geometry, + struct hikari_output *output, + const char *text, + float background[static 4]); + +void +hikari_indicator_bar_render(struct hikari_indicator_bar *indicator_bar, + struct hikari_render_data *render_data); + +void +hikari_indicator_bar_damage(struct hikari_indicator_bar *indicator_bar, + struct wlr_box *view_geometry, + struct hikari_output *output); + +#endif diff --git a/include/hikari/indicator_frame.h b/include/hikari/indicator_frame.h new file mode 100644 index 0000000..a8a8053 --- /dev/null +++ b/include/hikari/indicator_frame.h @@ -0,0 +1,25 @@ +#if !defined(HIKARIINDICATOR_FRAME_H) +#define HIKARIINDICATOR_FRAME_H + +#include + +struct hikari_view; +struct hikari_render_data; + +struct hikari_indicator_frame { + struct wlr_box top; + struct wlr_box bottom; + struct wlr_box left; + struct wlr_box right; +}; + +void +hikari_indicator_frame_refresh_geometry( + struct hikari_indicator_frame *indicator_frame, struct hikari_view *view); + +void +hikari_indicator_frame_render(struct hikari_indicator_frame *indicator_frame, + float color[static 4], + struct hikari_render_data *render_data); + +#endif diff --git a/include/hikari/input_buffer.h b/include/hikari/input_buffer.h new file mode 100644 index 0000000..f23841c --- /dev/null +++ b/include/hikari/input_buffer.h @@ -0,0 +1,36 @@ +#if !defined(HIKARI_INPUT_BUFFER_H) +#define HIKARI_INPUT_BUFFER_H + +#include +#include + +struct hikari_input_buffer { + char buffer[256]; + size_t pos; +}; + +struct hikari_input_buffer * +input_buffer_init(struct hikari_input_buffer *input_buffer, char *content); + +void +hikari_input_buffer_add_char( + struct hikari_input_buffer *input_buffer, char input); + +void +hikari_input_buffer_add_utf32_char( + struct hikari_input_buffer *input_buffer, uint32_t codepoint); + +void +hikari_input_buffer_replace( + struct hikari_input_buffer *input_buffer, char *content); + +void +hikari_input_buffer_remove_char(struct hikari_input_buffer *input_buffer); + +void +hikari_input_buffer_clear(struct hikari_input_buffer *input_buffer); + +char * +hikari_input_buffer_read(struct hikari_input_buffer *input_buffer); + +#endif diff --git a/include/hikari/keybinding.h b/include/hikari/keybinding.h new file mode 100644 index 0000000..24795cf --- /dev/null +++ b/include/hikari/keybinding.h @@ -0,0 +1,18 @@ +#if !defined(HIKARI_KEYBINDING_H) +#define HIKARI_KEYBINDING_H + +#include + +struct hikari_keybinding { + uint32_t keycode; + uint32_t modifiers; + + void (*action)(void *); + void (*cleanup)(void *); + void *arg; +}; + +void +hikari_keybinding_fini(struct hikari_keybinding *keybinding); + +#endif diff --git a/include/hikari/keyboard.h b/include/hikari/keyboard.h new file mode 100644 index 0000000..0915ebc --- /dev/null +++ b/include/hikari/keyboard.h @@ -0,0 +1,25 @@ +#if !defined(HIKARI_KEYBOARD_H) +#define HIKARI_KEYBOARD_H + +#include +#include + +#include + +struct hikari_keyboard { + struct wl_list link; + struct wlr_input_device *device; + + struct wl_listener modifiers; + struct wl_listener key; + struct wl_listener destroy; +}; + +void +hikari_keyboard_init( + struct hikari_keyboard *keyboard, struct wlr_input_device *device); + +void +hikari_keyboard_fini(struct hikari_keyboard *keyboard); + +#endif diff --git a/include/hikari/keyboard_grab_mode.h b/include/hikari/keyboard_grab_mode.h new file mode 100644 index 0000000..8091cc9 --- /dev/null +++ b/include/hikari/keyboard_grab_mode.h @@ -0,0 +1,14 @@ +#if !defined(HIKARI_KEYBOARD_GRAB_MODE_H) +#define HIKARI_KEYBOARD_GRAB_MODE_H + +#include + +struct hikari_keyboard_grab_mode { + struct hikari_mode mode; +}; + +void +hikari_keyboard_grab_mode_init( + struct hikari_keyboard_grab_mode *keyboard_grab_mode); + +#endif diff --git a/include/hikari/layout.h b/include/hikari/layout.h new file mode 100644 index 0000000..6d9a85c --- /dev/null +++ b/include/hikari/layout.h @@ -0,0 +1,27 @@ +#if !defined(HIKARI_LAYOUT_H) +#define HIKARI_LAYOUT_H + +#include + +struct hikari_split; +struct hikari_view; + +struct hikari_layout { + struct hikari_split *split; + + struct wl_list tiles; +}; + +void +hikari_layout_init(struct hikari_layout *layout, struct hikari_split *split); + +void +hikari_layout_fini(struct hikari_layout *layout); + +struct hikari_view * +hikari_layout_first_view(struct hikari_layout *layout); + +struct hikari_view * +hikari_layout_last_view(struct hikari_layout *layout); + +#endif diff --git a/include/hikari/mark.h b/include/hikari/mark.h new file mode 100644 index 0000000..2d87508 --- /dev/null +++ b/include/hikari/mark.h @@ -0,0 +1,54 @@ +#if !defined(HIKARI_MARK_H) +#define HIKARI_MARK_H + +struct hikari_view; + +struct hikari_mark { + char *name; + struct hikari_view *view; +}; + +static const int HIKARI_NR_OF_MARKS = 26; + +extern struct hikari_mark marks[HIKARI_NR_OF_MARKS]; + +static struct hikari_mark *const HIKARI_MARK_a = &marks[0]; +static struct hikari_mark *const HIKARI_MARK_b = &marks[1]; +static struct hikari_mark *const HIKARI_MARK_c = &marks[2]; +static struct hikari_mark *const HIKARI_MARK_d = &marks[3]; +static struct hikari_mark *const HIKARI_MARK_e = &marks[4]; +static struct hikari_mark *const HIKARI_MARK_f = &marks[5]; +static struct hikari_mark *const HIKARI_MARK_g = &marks[6]; +static struct hikari_mark *const HIKARI_MARK_h = &marks[7]; +static struct hikari_mark *const HIKARI_MARK_i = &marks[8]; +static struct hikari_mark *const HIKARI_MARK_j = &marks[9]; +static struct hikari_mark *const HIKARI_MARK_k = &marks[10]; +static struct hikari_mark *const HIKARI_MARK_l = &marks[11]; +static struct hikari_mark *const HIKARI_MARK_m = &marks[12]; +static struct hikari_mark *const HIKARI_MARK_n = &marks[13]; +static struct hikari_mark *const HIKARI_MARK_o = &marks[14]; +static struct hikari_mark *const HIKARI_MARK_p = &marks[15]; +static struct hikari_mark *const HIKARI_MARK_q = &marks[16]; +static struct hikari_mark *const HIKARI_MARK_r = &marks[17]; +static struct hikari_mark *const HIKARI_MARK_s = &marks[18]; +static struct hikari_mark *const HIKARI_MARK_t = &marks[19]; +static struct hikari_mark *const HIKARI_MARK_u = &marks[20]; +static struct hikari_mark *const HIKARI_MARK_v = &marks[21]; +static struct hikari_mark *const HIKARI_MARK_w = &marks[22]; +static struct hikari_mark *const HIKARI_MARK_x = &marks[23]; +static struct hikari_mark *const HIKARI_MARK_y = &marks[24]; +static struct hikari_mark *const HIKARI_MARK_z = &marks[25]; + +void +hikari_marks_init(void); + +void +hikari_marks_fini(void); + +void +hikari_mark_clear(struct hikari_mark *mark); + +void +hikari_mark_set(struct hikari_mark *mark, struct hikari_view *view); + +#endif diff --git a/include/hikari/mark_assign_mode.h b/include/hikari/mark_assign_mode.h new file mode 100644 index 0000000..6d5a96b --- /dev/null +++ b/include/hikari/mark_assign_mode.h @@ -0,0 +1,21 @@ +#if !defined(HIKARI_MARK_ASSIGN_MODE_H) +#define HIKARI_MARK_ASSIGN_MODE_H + +#include +#include + +struct hikari_mark; + +struct hikari_mark_assign_mode { + struct hikari_mode mode; + struct hikari_mark *pending_mark; + struct hikari_indicator indicator; +}; + +void +hikari_mark_assign_mode_init(struct hikari_mark_assign_mode *mark_assign_mode); + +void +hikari_mark_assign_mode_fini(struct hikari_mark_assign_mode *mark_assign_mode); + +#endif diff --git a/include/hikari/mark_select_mode.h b/include/hikari/mark_select_mode.h new file mode 100644 index 0000000..e9f0bd8 --- /dev/null +++ b/include/hikari/mark_select_mode.h @@ -0,0 +1,17 @@ +#if !defined(HIKARI_MARK_SELECT_MODE_H) +#define HIKARI_MARK_SELECT_MODE_H + +#include + +#include + +struct hikari_mark_select_mode { + struct hikari_mode mode; + + bool switch_workspace; +}; + +void +hikari_mark_select_mode_init(struct hikari_mark_select_mode *mark_select_mode); + +#endif diff --git a/include/hikari/maximized_state.h b/include/hikari/maximized_state.h new file mode 100644 index 0000000..7ae543b --- /dev/null +++ b/include/hikari/maximized_state.h @@ -0,0 +1,38 @@ +#if !defined(HIKARIMAXIMIZED_STATE_H) +#define HIKARIMAXIMIZED_STATE_H + +#include + +#include + +#include + +struct hikari_view; + +enum hikari_maximization { + HIKARI_MAXIMIZATION_FULLY_MAXIMIZED, + HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED, + HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED +}; + +struct hikari_maximized_state { + enum hikari_maximization maximization; + struct wlr_box geometry; +}; + +struct hikari_maximized_state * +hikari_maximized_state_full(struct hikari_view *view, int width, int height); + +static inline struct hikari_maximized_state * +hikari_maximized_state_alloc(void) +{ + return hikari_malloc(sizeof(struct hikari_maximized_state)); +} + +static inline void +hikari_maximized_state_destroy(struct hikari_maximized_state *maximized_state) +{ + hikari_free(maximized_state); +} + +#endif diff --git a/include/hikari/memory.h b/include/hikari/memory.h new file mode 100644 index 0000000..0bb3e92 --- /dev/null +++ b/include/hikari/memory.h @@ -0,0 +1,15 @@ +#if !defined(HIKARI_MEMORY_H) +#define HIKARI_MEMORY_H + +#include + +void * +hikari_malloc(size_t size); + +void * +hikari_calloc(size_t number, size_t size); + +void +hikari_free(void *ptr); + +#endif diff --git a/include/hikari/mode.h b/include/hikari/mode.h new file mode 100644 index 0000000..3f83820 --- /dev/null +++ b/include/hikari/mode.h @@ -0,0 +1,36 @@ +#if !defined(HIKARI_MODE_H) +#define HIKARI_MODE_H + +#include + +struct hikari_keyboard; +struct hikari_output; +struct hikari_render_data; +struct hikari_workspace; + +enum hikari_mode_type { + HIKARI_MODE_TYPE_NORMAL, + HIKARI_MODE_TYPE_GROUP_CHANGE, + HIKARI_MODE_TYPE_ASSIGN_MARK, + HIKARI_MODE_TYPE_SELECT_MARK, + HIKARI_MODE_TYPE_SELECT_EXEC, + HIKARI_MODE_TYPE_GRAB_KEYBOARD, + HIKARI_MODE_TYPE_MOVE, + HIKARI_MODE_TYPE_TILING +}; + +struct hikari_mode { + enum hikari_mode_type type; + + void (*key_handler)(struct wl_listener *listener, void *data); + void (*modifier_handler)(struct wl_listener *listener, void *data); + void (*button_handler)(struct wl_listener *listener, void *data); + + void (*cancel)(void); + void (*cursor_move)(void); + + void (*render)( + struct hikari_output *output, struct hikari_render_data *render_data); +}; + +#endif diff --git a/include/hikari/move_mode.h b/include/hikari/move_mode.h new file mode 100644 index 0000000..fa39134 --- /dev/null +++ b/include/hikari/move_mode.h @@ -0,0 +1,15 @@ +#if !defined(HIKARI_MOVE_MODE_H) +#define HIKARI_MOVE_MODE_H + +#include + +struct hikari_keybinding; + +struct hikari_move_mode { + struct hikari_mode mode; +}; + +void +hikari_move_mode_init(struct hikari_move_mode *move_mode); + +#endif diff --git a/include/hikari/normal_mode.h b/include/hikari/normal_mode.h new file mode 100644 index 0000000..bfee99f --- /dev/null +++ b/include/hikari/normal_mode.h @@ -0,0 +1,15 @@ +#if !defined(HIKARI_NORMAL_MODE_H) +#define HIKARI_NORMAL_MODE_H + +#include + +struct hikari_keybinding; + +struct hikari_normal_mode { + struct hikari_mode mode; +}; + +void +hikari_normal_mode_init(struct hikari_normal_mode *normal_mode); + +#endif diff --git a/include/hikari/operation.h b/include/hikari/operation.h new file mode 100644 index 0000000..3ce960d --- /dev/null +++ b/include/hikari/operation.h @@ -0,0 +1,27 @@ +#if !defined(HIKARI_OPERATION_H) +#define HIKARI_OPERATION_H + +#include + +struct hikari_tile; + +enum hikari_operation_type { + HIKARI_OPERATION_TYPE_RESIZE, + HIKARI_OPERATION_TYPE_RESET, + HIKARI_OPERATION_TYPE_UNMAXIMIZE, + HIKARI_OPERATION_TYPE_FULL_MAXIMIZE, + HIKARI_OPERATION_TYPE_VERTICAL_MAXIMIZE, + HIKARI_OPERATION_TYPE_HORIZONTAL_MAXIMIZE, + HIKARI_OPERATION_TYPE_TILE +}; + +struct hikari_operation { + enum hikari_operation_type type; + bool dirty; + bool center; + uint32_t serial; + struct wlr_box geometry; + struct hikari_tile *tile; +}; + +#endif diff --git a/include/hikari/output.h b/include/hikari/output.h new file mode 100644 index 0000000..889143d --- /dev/null +++ b/include/hikari/output.h @@ -0,0 +1,60 @@ +#if !defined(HIKARI_OUTPUT_H) +#define HIKARI_OUTPUT_H + +#include + +#include +#include + +#include +#include + +struct hikari_output { + struct wl_list link; + struct wlr_output *output; + struct wlr_output_damage *damage; + struct hikari_workspace *workspace; + + struct wl_listener damage_frame; + /* struct wl_listener mode; */ + struct wl_listener destroy; + + struct wl_list views; +#ifdef HAVE_XWAYLAND + struct wl_list unmanaged_xwayland_views; +#endif + struct wl_list server_outputs; + + struct wlr_box geometry; +}; + +void +hikari_output_init(struct hikari_output *output, struct wlr_output *wlr_output); + +void +hikari_output_fini(struct hikari_output *output); + +void +hikari_output_scissor_render(struct wlr_output *wlr_output, + struct wlr_renderer *renderer, + pixman_box32_t *rect); + +void +hikari_output_damage_whole(struct hikari_output *output); + +void +hikari_output_disable(struct hikari_output *output); + +void +hikari_output_enable(struct hikari_output *output); + +static inline void +hikari_output_add_damage(struct hikari_output *output, struct wlr_box *region) +{ + assert(output != NULL); + assert(region != NULL); + + wlr_output_damage_add_box(output->damage, region); +} + +#endif diff --git a/include/hikari/pointer_config.h b/include/hikari/pointer_config.h new file mode 100644 index 0000000..882ab47 --- /dev/null +++ b/include/hikari/pointer_config.h @@ -0,0 +1,20 @@ +#if !defined(HIKARI_POINTER_CONFIG_H) +#define HIKARI_POINTER_CONFIG_H + +#include +#include + +struct hikari_pointer_config { + struct wl_list link; + + char *name; + + double accel; + enum libinput_config_scroll_method scroll_method; + uint32_t scroll_button; +}; + +void +hikari_pointer_config_fini(struct hikari_pointer_config *pointer_config); + +#endif diff --git a/include/hikari/render_data.h b/include/hikari/render_data.h new file mode 100644 index 0000000..40ddbf3 --- /dev/null +++ b/include/hikari/render_data.h @@ -0,0 +1,18 @@ +#if !defined(HIKARI_RENDER_DATA_H) +#define HIKARI_RENDER_DATA_H + +#include + +struct wlr_output *output; +struct wlr_renderer *renderer; +struct timespec *when; + +struct hikari_render_data { + struct wlr_output *output; + struct wlr_renderer *renderer; + pixman_region32_t *damage; + struct wlr_box *geometry; + struct timespec *when; +}; + +#endif diff --git a/include/hikari/resize_mode.h b/include/hikari/resize_mode.h new file mode 100644 index 0000000..9a8e9a4 --- /dev/null +++ b/include/hikari/resize_mode.h @@ -0,0 +1,15 @@ +#if !defined(HIKARI_RESIZE_MODE_H) +#define HIKARI_RESIZE_MODE_H + +#include + +struct hikari_keybinding; + +struct hikari_resize_mode { + struct hikari_mode mode; +}; + +void +hikari_resize_mode_init(struct hikari_resize_mode *resize_mode); + +#endif diff --git a/include/hikari/server.h b/include/hikari/server.h new file mode 100644 index 0000000..013de17 --- /dev/null +++ b/include/hikari/server.h @@ -0,0 +1,294 @@ +#if !defined(HIKARI_SERVER_H) +#define HIKARI_SERVER_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct wlr_input_device; +struct wlr_xcursor_manager; + +struct hikari_output; +struct hikari_group; + +struct hikari_server { + bool locked; + bool cycling; +#ifndef NDEBUG + bool track_damage; +#endif + + const char *socket; + + struct hikari_indicator indicator; + + struct wl_display *display; + struct wl_event_loop *event_loop; + struct wlr_backend *backend; + struct wlr_renderer *renderer; + struct wlr_xdg_output_manager_v1 *output_manager; + struct wlr_data_device_manager *data_device_manager; + + struct wl_listener new_output; + struct wl_listener new_input; + struct wl_listener new_xdg_surface; + struct wl_listener cursor_motion_absolute; + struct wl_listener cursor_motion; + struct wl_listener cursor_frame; + struct wl_listener cursor_axis; + struct wl_listener cursor_button; + struct wl_listener request_set_primary_selection; + struct wl_listener request_set_selection; + struct wl_listener output_layout_change; + struct wl_listener new_decoration; +#ifdef HAVE_XWAYLAND + struct wl_listener new_xwayland_surface; + + struct wlr_xwayland *xwayland; +#endif + + struct wlr_compositor *compositor; + struct wlr_server_decoration_manager *decoration_manager; + + struct wlr_xdg_shell *xdg_shell; + struct wlr_output_layout *output_layout; + struct wlr_seat *seat; + + struct hikari_workspace *workspace; + + struct wlr_cursor *cursor; + struct wlr_xcursor_manager *cursor_mgr; + + struct wl_list keyboards; + struct wl_list outputs; + + struct wl_list groups; + struct wl_list visible_groups; + struct wl_list workspaces; + + struct hikari_mode *mode; + + struct hikari_normal_mode normal_mode; + struct hikari_mark_select_mode mark_select_mode; + struct hikari_mark_assign_mode mark_assign_mode; + struct hikari_exec_select_mode exec_select_mode; + struct hikari_group_assign_mode group_assign_mode; + struct hikari_keyboard_grab_mode keyboard_grab_mode; + struct hikari_tiling_mode tiling_mode; + struct hikari_move_mode move_mode; + struct hikari_resize_mode resize_mode; + + struct { + uint32_t modifiers; + bool mod_released; + bool mod_changed; + bool mod_pressed; + } keyboard_state; +}; + +extern struct hikari_server hikari_server; + +static inline bool +hikari_server_is_cycling(void) +{ + return hikari_server.cycling; +} + +static inline void +hikari_server_set_cycling(void) +{ + hikari_server.cycling = true; +} + +static inline void +hikari_server_unset_cycling(void) +{ + hikari_server.cycling = false; +} + +void +hikari_server_activate_cursor(void); + +void +hikari_server_deactivate_cursor(void); + +void +hikari_server_start(void); + +void +hikari_server_stop(); + +void +hikari_server_terminate(void *arg); + +void +hikari_server_cursor_focus(void); + +void +hikari_server_lock(void *arg); + +void +hikari_server_execute_command(void *arg); + +void +hikari_server_unlock(void); + +struct hikari_group * +hikari_server_find_group(const char *group_name); + +struct hikari_group * +hikari_server_find_or_create_group(const char *group_name); + +#define SHEET_ACTIONS(n) \ + static inline void hikari_server_display_sheet_##n(void *arg) \ + { \ + hikari_workspace_display_sheet_##n(hikari_server.workspace); \ + } \ + \ + static inline void hikari_server_pin_view_to_sheet_##n(void *arg) \ + { \ + hikari_workspace_pin_view_to_sheet_##n(hikari_server.workspace); \ + } + +SHEET_ACTIONS(0) +SHEET_ACTIONS(1) +SHEET_ACTIONS(2) +SHEET_ACTIONS(3) +SHEET_ACTIONS(4) +SHEET_ACTIONS(5) +SHEET_ACTIONS(6) +SHEET_ACTIONS(7) +SHEET_ACTIONS(8) +SHEET_ACTIONS(9) +SHEET_ACTIONS(alternate) +SHEET_ACTIONS(current) +SHEET_ACTIONS(next) +SHEET_ACTIONS(prev) +#undef SHEET_ACTIONS + +#define MOVE_RESIZE_ACTIONS(dir, x, y) \ + static inline void hikari_server_move_view_##dir(void *arg) \ + { \ + hikari_workspace_move_view(hikari_server.workspace, x, y); \ + } \ + \ + static inline void hikari_server_decrease_view_size_##dir(void *arg) \ + { \ + hikari_workspace_resize_view(hikari_server.workspace, x, y); \ + } \ + \ + static inline void hikari_server_increase_view_size_##dir(void *arg) \ + { \ + hikari_workspace_resize_view(hikari_server.workspace, x, y); \ + } \ + \ + static inline void hikari_server_snap_view_##dir(void *arg) \ + { \ + hikari_workspace_snap_view_##dir(hikari_server.workspace); \ + } + +MOVE_RESIZE_ACTIONS(up, 0, -100) +MOVE_RESIZE_ACTIONS(down, 0, 100) +MOVE_RESIZE_ACTIONS(left, -100, 0) +MOVE_RESIZE_ACTIONS(right, 100, 0) +#undef MOVE_RESIZE_ACTIONS + +#define CYCLE_ACTION(n) void hikari_server_cycle_##n(void *arg); + +CYCLE_ACTION(first_group_view) +CYCLE_ACTION(last_group_view) +CYCLE_ACTION(next_group_view) +CYCLE_ACTION(prev_group_view) +CYCLE_ACTION(next_layout_view) +CYCLE_ACTION(prev_layout_view) +CYCLE_ACTION(first_layout_view) +CYCLE_ACTION(last_layout_view) +CYCLE_ACTION(next_group) +CYCLE_ACTION(prev_group) +CYCLE_ACTION(next_sheet_view) +CYCLE_ACTION(prev_sheet_view) +#undef CYCLE_ACTION + +#define WORKSPACE_ACTION(name) \ + static inline void hikari_server_##name(void *arg) \ + { \ + hikari_workspace_##name(hikari_server.workspace); \ + } + +WORKSPACE_ACTION(toggle_view_vertical_maximize) +WORKSPACE_ACTION(toggle_view_horizontal_maximize) +WORKSPACE_ACTION(toggle_view_full_maximize) +WORKSPACE_ACTION(toggle_view_floating) +WORKSPACE_ACTION(toggle_view_iconified) +WORKSPACE_ACTION(only_view) +WORKSPACE_ACTION(only_group) +WORKSPACE_ACTION(hide_view) +WORKSPACE_ACTION(hide_group) +WORKSPACE_ACTION(show_group) +WORKSPACE_ACTION(raise_view) +WORKSPACE_ACTION(raise_group) +WORKSPACE_ACTION(lower_view) +WORKSPACE_ACTION(lower_group) +WORKSPACE_ACTION(show_iconified_sheet_views) +WORKSPACE_ACTION(show_all_iconified_views) +WORKSPACE_ACTION(reset_view_geometry) +WORKSPACE_ACTION(quit_view) +WORKSPACE_ACTION(exchange_next_view) +WORKSPACE_ACTION(exchange_prev_view) +WORKSPACE_ACTION(exchange_main_layout_view) +WORKSPACE_ACTION(show_all_group_views) +WORKSPACE_ACTION(switch_to_next_inhabited_sheet) +WORKSPACE_ACTION(switch_to_prev_inhabited_sheet) +WORKSPACE_ACTION(show_all_sheet_views) +#undef WORKSPACE_ACTION + +static inline void +hikari_server_clear_workspace(void *arg) +{ + hikari_workspace_clear(hikari_server.workspace); +} + +#define MODE(name) \ + void hikari_server_enter_##name##_mode(void *arg); \ + \ + static inline bool hikari_server_in_##name##_mode(void) \ + { \ + return hikari_server.mode == \ + (struct hikari_mode *)&hikari_server.name##_mode; \ + } + +MODE(normal) +MODE(group_assign) +MODE(keyboard_grab) +MODE(tiling) +MODE(mark_assign) +MODE(mark_select) +MODE(exec_select) +MODE(move) +MODE(resize) + +void +hikari_server_enter_mark_select_switch_mode(void *arg); +#undef MODE + +void +hikari_server_refresh_indication(void); + +void +hikari_server_reset_sheet_layout(void *arg); + +void +hikari_server_layout_sheet(void *arg); + +#endif diff --git a/include/hikari/sheet.h b/include/hikari/sheet.h new file mode 100644 index 0000000..54b002b --- /dev/null +++ b/include/hikari/sheet.h @@ -0,0 +1,92 @@ +#if !defined(HIKARI_SHEET_H) +#define HIKARI_SHEET_H + +#include + +#include + +struct hikari_group; +struct hikari_layout; +struct hikari_split; +struct hikari_workspace; + +struct hikari_sheet { + uint8_t nr; + struct wl_list views; + struct hikari_layout *layout; + + struct hikari_group *group; + struct hikari_workspace *workspace; +}; + +void +hikari_sheet_init( + struct hikari_sheet *sheet, int nr, struct hikari_workspace *workspace); + +void +hikari_sheet_fini(struct hikari_sheet *sheet); + +static const int HIKARI_NR_OF_SHEETS = 10; + +struct hikari_view * +hikari_sheet_first_view(struct hikari_sheet *sheet); + +struct hikari_view * +hikari_sheet_last_view(struct hikari_sheet *sheet); + +struct hikari_view * +hikari_sheet_next_view(struct hikari_sheet *sheet, struct hikari_view *view); + +struct hikari_view * +hikari_sheet_prev_view(struct hikari_sheet *sheet, struct hikari_view *view); + +struct hikari_view * +hikari_sheet_first_tileable_view(struct hikari_sheet *sheet); + +struct hikari_view * +hikari_sheet_vertical_layout(struct hikari_sheet *sheet, + struct hikari_view *first, + struct wlr_box *frame, + int max); + +struct hikari_view * +hikari_sheet_horizontal_layout(struct hikari_sheet *sheet, + struct hikari_view *first, + struct wlr_box *frame, + int max); + +struct hikari_view * +hikari_sheet_grid_layout(struct hikari_sheet *sheet, + struct hikari_view *first, + struct wlr_box *frame, + int max); + +struct hikari_view * +hikari_sheet_full_layout(struct hikari_sheet *sheet, + struct hikari_view *first, + struct wlr_box *frame, + int max); + +int +hikari_sheet_tileable_views(struct hikari_sheet *sheet); + +void +hikari_sheet_reset_layout(struct hikari_sheet *sheet); + +struct hikari_sheet * +hikari_sheet_next(struct hikari_sheet *sheet); + +struct hikari_sheet * +hikari_sheet_prev(struct hikari_sheet *sheet); + +struct hikari_sheet * +hikari_sheet_next_inhabited(struct hikari_sheet *sheet); + +struct hikari_sheet * +hikari_sheet_prev_inhabited(struct hikari_sheet *sheet); + +void +hikari_sheet_apply_split( + struct hikari_sheet *sheet, struct hikari_split *split); + +#endif diff --git a/include/hikari/split.h b/include/hikari/split.h new file mode 100644 index 0000000..244e404 --- /dev/null +++ b/include/hikari/split.h @@ -0,0 +1,82 @@ +#if !defined(HIKARI_SPLIT_H) +#define HIKARI_SPLIT_H + +#include + +struct hikari_render_data; +struct hikari_sheet; +struct hikari_view; + +typedef struct hikari_view *(*layout_func_t)( + struct hikari_sheet *, struct hikari_view *, struct wlr_box *, int); + +enum hikari_split_type { + HIKARI_SPLIT_TYPE_VERTICAL, + HIKARI_SPLIT_TYPE_HORIZONTAL, + HIKARI_SPLIT_TYPE_CONTAINER +}; + +struct hikari_split { + enum hikari_split_type type; +}; + +struct hikari_vertical_split { + struct hikari_split split; + + float factor; + + struct hikari_split *left; + struct hikari_split *right; +}; + +struct hikari_horizontal_split { + struct hikari_split split; + + float factor; + + struct hikari_split *top; + struct hikari_split *bottom; +}; + +struct hikari_container { + struct hikari_split split; + struct wlr_box geometry; + + int max; + + layout_func_t layout; +}; + +void +hikari_split_apply(struct hikari_split *split, + struct wlr_box *geometry, + struct hikari_view *first); + +void +hikari_container_init( + struct hikari_container *container, int nr_of_views, layout_func_t layout); + +void +hikari_split_fini(struct hikari_split *split); + +void +hikari_vertical_split_init(struct hikari_vertical_split *vertical_split, + float factor, + struct hikari_split *left, + struct hikari_split *right); + +void +hikari_horizontal_split_init(struct hikari_horizontal_split *horizontal_split, + float factor, + struct hikari_split *left, + struct hikari_split *right); + +void +hikari_split_render( + struct hikari_split *split, struct hikari_render_data *render_data); + +void +hikari_container_render( + struct hikari_container *container, struct hikari_render_data *render_data); + +#endif diff --git a/include/hikari/tile.h b/include/hikari/tile.h new file mode 100644 index 0000000..a9931c9 --- /dev/null +++ b/include/hikari/tile.h @@ -0,0 +1,32 @@ +#if !defined(HIKARI_TILE_H) +#define HIKARI_TILE_H + +#include + +struct hikari_layout; +struct hikari_view; + +struct hikari_tile { + struct hikari_view *view; + struct wlr_box view_geometry; + struct wlr_box tile_geometry; + + struct wl_list layout_tiles; +}; + +void +hikari_tile_init(struct hikari_tile *tile, + struct hikari_view *view, + struct wlr_box *tile_geometry, + struct wlr_box *view_geometry); + +void +hikari_tile_fini(struct hikari_tile *tile); + +struct hikari_view * +hikari_tile_next_view(struct hikari_tile *tile); + +struct hikari_view * +hikari_tile_prev_view(struct hikari_tile *tile); + +#endif diff --git a/include/hikari/tiling_mode.h b/include/hikari/tiling_mode.h new file mode 100644 index 0000000..bdc003b --- /dev/null +++ b/include/hikari/tiling_mode.h @@ -0,0 +1,13 @@ +#if !defined(HIKARI_TILING_MODE_H) +#define HIKARI_TILING_MODE_H + +#include + +struct hikari_tiling_mode { + struct hikari_mode mode; +}; + +void +hikari_tiling_mode_init(struct hikari_tiling_mode *tiling_mode); + +#endif diff --git a/include/hikari/unlocker.h b/include/hikari/unlocker.h new file mode 100644 index 0000000..7b83093 --- /dev/null +++ b/include/hikari/unlocker.h @@ -0,0 +1,18 @@ +#if !defined(HIKARI_UNLOCKER_H) +#define HIKARI_UNLOCKER_H + +#include + +void +hikari_unlocker_init(void); + +void +hikari_unlocker_fini(void); + +void +hikari_unlocker_start(void); + +void +hikari_unlocker_key_handler(struct wl_listener *listener, void *data); + +#endif diff --git a/include/hikari/utf8.h b/include/hikari/utf8.h new file mode 100644 index 0000000..0e2da89 --- /dev/null +++ b/include/hikari/utf8.h @@ -0,0 +1,42 @@ +#if !defined(HIKARI_UTF8_H) +#define HIKARI_UTF8_H + +#include + +static inline size_t +utf8_chsize(uint32_t codepoint) +{ + if (codepoint < 0x80) { + return 1; + } else if (codepoint < 0x800) { + return 2; + } else if (codepoint < 0x10000) { + return 3; + } + return 4; +} + +static inline void +utf8_encode(char *buffer, size_t length, uint32_t codepoint) +{ + uint8_t first; + + if (codepoint < 0x80) { + first = 0; + } else if (codepoint < 0x800) { + first = 0xc0; + } else if (codepoint < 0x10000) { + first = 0xe0; + } else { + first = 0xf0; + } + + for (size_t i = length - 1; i > 0; --i) { + buffer[i] = (codepoint & 0x3f) | 0x80; + codepoint >>= 6; + } + + buffer[0] = codepoint | first; +} + +#endif diff --git a/include/hikari/view.h b/include/hikari/view.h new file mode 100644 index 0000000..b9a6a03 --- /dev/null +++ b/include/hikari/view.h @@ -0,0 +1,323 @@ +#if !defined(HIKARI_VIEW_H) +#define HIKARI_VIEW_H + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct hikari_mark; +struct hikari_render_data; + +enum hikari_view_type { HIKARI_XDG_VIEW, HIKARI_XWAYLAND_VIEW }; + +struct hikari_view; + +struct hikari_view_decoration { + struct wlr_server_decoration *wlr_decoration; + struct hikari_view *view; + struct wl_listener mode; +}; + +struct hikari_view { + struct hikari_view_interface view_interface; + enum hikari_view_type type; + + struct hikari_sheet *sheet; + struct hikari_group *group; + struct hikari_mark *mark; + struct hikari_output *output; + struct wlr_surface *surface; + + bool use_csd; + unsigned int flags; + char *title; + struct hikari_border border; + struct hikari_indicator_frame indicator_frame; + struct hikari_tile *tile; + + struct wlr_box geometry; + struct hikari_maximized_state *maximized_state; + + struct wl_list children; + + struct wl_list output_views; + struct wl_list workspace_views; + struct wl_list sheet_views; + struct wl_list group_views; + struct wl_list visible_group_views; + + struct hikari_operation pending_operation; + + struct wlr_box *current_geometry; + struct wlr_box *current_unmaximized_geometry; + + struct hikari_view_decoration decoration; + + uint32_t (*resize)(struct hikari_view *, int, int); + void (*move)(struct hikari_view *, int, int); + void (*activate)(struct hikari_view *, bool); + void (*quit)(struct hikari_view *); + void (*hide)(struct hikari_view *); + void (*show)(struct hikari_view *); + void (*constraints)(struct hikari_view *, int *, int *, int *, int *); +}; + +struct hikari_view_child { + struct wl_list link; + struct wlr_surface *surface; + struct hikari_view *parent; + + struct wl_listener commit; + struct wl_listener new_subsurface; +}; + +void +hikari_view_child_init(struct hikari_view_child *view_child, + struct hikari_view *parent, + struct wlr_surface *surface); + +void +hikari_view_child_fini(struct hikari_view_child *view_child); + +struct hikari_view_subsurface { + struct hikari_view_child view_child; + + struct wlr_subsurface *subsurface; + + struct wl_listener destroy; +}; + +void +hikari_view_subsurface_init(struct hikari_view_subsurface *view_subsurface, + struct hikari_view *parent, + struct wlr_subsurface *subsurface); + +void +hikari_view_subsurface_fini(struct hikari_view_subsurface *view_subsurface); + +#define FLAG(name, shift) \ + static const unsigned long hikari_view_##name##_flag = 1UL << shift; \ + \ + static inline bool hikari_view_is_##name(struct hikari_view *view) \ + { \ + assert(view != NULL); \ + return (view->flags & hikari_view_##name##_flag); \ + } \ + \ + static inline void hikari_view_set_##name(struct hikari_view *view) \ + { \ + assert(view != NULL); \ + view->flags |= hikari_view_##name##_flag; \ + } \ + \ + static inline void hikari_view_unset_##name(struct hikari_view *view) \ + { \ + assert(view != NULL); \ + view->flags &= ~hikari_view_##name##_flag; \ + } + +FLAG(hidden, 0UL) +FLAG(iconified, 1UL) +FLAG(floating, 2UL) +#undef FLAG + +void +hikari_view_init(struct hikari_view *view, + enum hikari_view_type type, + struct hikari_workspace *workspace); + +void +hikari_view_fini(struct hikari_view *view); + +void +hikari_view_manage(struct hikari_view *view, + struct hikari_sheet *sheet, + struct hikari_group *group); + +void +hikari_view_set_title(struct hikari_view *view, const char *title); + +#define VIEW_ACTION(name) void hikari_view_##name(struct hikari_view *view); + +VIEW_ACTION(show) +VIEW_ACTION(hide) +VIEW_ACTION(raise) +VIEW_ACTION(raise_hidden) +VIEW_ACTION(lower) +VIEW_ACTION(toggle_full_maximize) +VIEW_ACTION(toggle_vertical_maximize) +VIEW_ACTION(toggle_horizontal_maximize) +VIEW_ACTION(toggle_floating) +VIEW_ACTION(damage_whole) +VIEW_ACTION(commit_pending_geometry) +VIEW_ACTION(top_left_cursor) +VIEW_ACTION(bottom_right_cursor) +VIEW_ACTION(center_cursor) +VIEW_ACTION(toggle_iconified) +VIEW_ACTION(reset_geometry) +#undef VIEW_ACTION + +void +hikari_view_pin_to_sheet(struct hikari_view *view, struct hikari_sheet *sheet); + +void +hikari_view_group(struct hikari_view *view, struct hikari_group *group); + +void +hikari_view_resize(struct hikari_view *view, int width, int height); + +void +hikari_view_move(struct hikari_view *view, int x, int y); + +void +hikari_view_move_absolute(struct hikari_view *view, int x, int y); + +void +hikari_view_resize_absolute(struct hikari_view *view, int x, int y); + +void +hikari_view_assign_sheet(struct hikari_view *view, struct hikari_sheet *sheet); + +void +hikari_view_tile(struct hikari_view *view, struct wlr_box *geometry); + +void +hikari_view_exchange(struct hikari_view *from, struct hikari_view *to); + +void +hikari_view_commit_pending_operation(struct hikari_view *view); + +void +hikari_view_damage_surface( + struct hikari_view *view, struct wlr_surface *surface, bool whole); + +void +hikari_view_refresh_geometry( + struct hikari_view *view, struct wlr_box *geometry); + +void +hikari_view_activate(struct hikari_view *view, bool active); + +static inline bool +hikari_view_is_dirty(struct hikari_view *view) +{ + assert(view != NULL); + return view->pending_operation.dirty; +} + +static inline void +hikari_view_set_dirty(struct hikari_view *view) +{ + assert(view != NULL); + view->pending_operation.dirty = true; +} + +static inline void +hikari_view_unset_dirty(struct hikari_view *view) +{ + assert(view != NULL); + view->pending_operation.dirty = false; +} + +static inline bool +hikari_view_was_updated(struct hikari_view *view, uint32_t serial) +{ + assert(view != NULL); + return hikari_view_is_dirty(view) && serial >= view->pending_operation.serial; +} + +static inline void +hikari_view_quit(struct hikari_view *view) +{ + assert(view != NULL); + if (view->quit) { + view->quit(view); + } +} + +static inline bool +hikari_view_is_fully_maximized(struct hikari_view *view) +{ + assert(view != NULL); + return view->maximized_state != NULL && + view->maximized_state->maximization == + HIKARI_MAXIMIZATION_FULLY_MAXIMIZED; +} + +static inline void +hikari_view_for_each_surface( + struct hikari_view *view, wlr_surface_iterator_func_t func, void *data) +{ + assert(view != NULL); + assert(view->surface != NULL); + + wlr_surface_for_each_surface(view->surface, func, data); +} + +static inline struct wlr_box * +hikari_view_geometry(struct hikari_view *view) +{ + assert(view != NULL); + return view->current_geometry; +} + +static inline struct wlr_box * +hikari_view_border_geometry(struct hikari_view *view) +{ + assert(view != NULL); + return &view->border.geometry; +} + +static inline bool +hikari_view_has_focus(struct hikari_view *view) +{ + assert(view != NULL); + return hikari_server.workspace->focus_view == view; +} + +static inline bool +hikari_view_wants_border(struct hikari_view *view) +{ + assert(view != NULL); + return view->border.state != HIKARI_BORDER_NONE; +} + +static inline bool +hikari_view_is_tileable(struct hikari_view *view) +{ + assert(view != NULL); + return !(view->flags & + (hikari_view_hidden_flag | hikari_view_floating_flag)) && + !hikari_view_is_dirty(view); +} + +static inline bool +hikari_view_is_tiled(struct hikari_view *view) +{ + assert(view != NULL); + return view->tile != NULL; +} + +static inline bool +hikari_view_is_mapped(struct hikari_view *view) +{ + assert(view != NULL); + return view->surface != NULL; +} + +#endif diff --git a/include/hikari/view_autoconf.h b/include/hikari/view_autoconf.h new file mode 100644 index 0000000..1b6f9e3 --- /dev/null +++ b/include/hikari/view_autoconf.h @@ -0,0 +1,30 @@ +#if !defined(HIKARI_VIEW_AUTOCONF_H) +#define HIKARI_VIEW_AUTOCONF_H + +#include +#include + +struct hikari_mark; + +struct hikari_view_autoconf { + struct wl_list link; + + char *app_id; + char *group_name; + + int sheet_nr; + + struct hikari_mark *mark; + + struct { + int x; + int y; + } position; + + bool focus; +}; + +void +hikari_view_autoconf_fini(struct hikari_view_autoconf *autoconf); + +#endif diff --git a/include/hikari/view_interface.h b/include/hikari/view_interface.h new file mode 100644 index 0000000..7c82965 --- /dev/null +++ b/include/hikari/view_interface.h @@ -0,0 +1,46 @@ +#if !defined(HIKARI_VIEW_INTERFACE_H) +#define HIKARI_VIEW_INTERFACE_H + +struct wlr_surface; + +struct hikari_view_interface { + struct wlr_surface *(*surface_at)( + struct hikari_view_interface *view_interface, + double ox, + double oy, + double *sx, + double *sy); + + void (*focus)(struct hikari_view_interface *view_interface); + + void (*for_each_surface)(struct hikari_view_interface *view_interface, + void (*func)(struct wlr_surface *, int, int, void *), + void *data); +}; + +static inline struct wlr_surface * +hikari_view_interface_surface_at(struct hikari_view_interface *view_interface, + double ox, + double oy, + double *sx, + double *sy) +{ + return view_interface->surface_at(view_interface, ox, oy, sx, sy); +} + +static inline void +hikari_view_interface_focus(struct hikari_view_interface *view_interface) +{ + view_interface->focus(view_interface); +} + +static inline void +hikari_view_interface_for_each_surface( + struct hikari_view_interface *view_interface, + void (*func)(struct wlr_surface *, int, int, void *), + void *data) +{ + view_interface->for_each_surface(view_interface, func, data); +} + +#endif diff --git a/include/hikari/workspace.h b/include/hikari/workspace.h new file mode 100644 index 0000000..4c9d3de --- /dev/null +++ b/include/hikari/workspace.h @@ -0,0 +1,142 @@ +#if !defined(HIKARI_WORKSPACE_H) +#define HIKARI_WORKSPACE_H + +#include +#include + +struct hikari_output; +struct hikari_render_data; +struct hikari_sheet; +struct hikari_view; + +struct wlr_texture; + +struct hikari_workspace { + struct hikari_sheet *sheet; + struct hikari_sheet *alternate_sheet; + struct hikari_output *output; + struct hikari_sheet *sheets; + struct hikari_view *focus_view; + + struct wlr_texture *background; + + struct wl_list views; + + struct wl_list server_workspaces; +}; + +void +hikari_workspace_init( + struct hikari_workspace *workspace, struct hikari_output *output); + +void +hikari_workspace_fini(struct hikari_workspace *workspace); + +void +hikari_workspace_render_background( + struct hikari_workspace *workspace, struct hikari_render_data *render_data); + +void +hikari_workspace_focus_view( + struct hikari_workspace *workspace, struct hikari_view *view); + +void +hikari_workspace_move_view(struct hikari_workspace *workspace, int dx, int dy); + +void +hikari_workspace_resize_view( + struct hikari_workspace *workspace, int dwidth, int dheight); + +#define WORKSPACE_CYCLE_ACTION(name) \ + struct hikari_view *hikari_workspace_##name( \ + struct hikari_workspace *workspace); + +WORKSPACE_CYCLE_ACTION(first_view) +WORKSPACE_CYCLE_ACTION(last_view) +WORKSPACE_CYCLE_ACTION(next_view) +WORKSPACE_CYCLE_ACTION(prev_view) +WORKSPACE_CYCLE_ACTION(next_layout_view) +WORKSPACE_CYCLE_ACTION(prev_layout_view) +WORKSPACE_CYCLE_ACTION(first_layout_view) +WORKSPACE_CYCLE_ACTION(last_layout_view) +WORKSPACE_CYCLE_ACTION(first_group_view) +WORKSPACE_CYCLE_ACTION(last_group_view) +WORKSPACE_CYCLE_ACTION(next_group_view) +WORKSPACE_CYCLE_ACTION(prev_group_view) +WORKSPACE_CYCLE_ACTION(next_group) +WORKSPACE_CYCLE_ACTION(prev_group) +WORKSPACE_CYCLE_ACTION(next_sheet_view) +WORKSPACE_CYCLE_ACTION(prev_sheet_view) +#undef WORKSPACE_CYCLE_ACTION + +#define WORKSPACE_ACTION(name) \ + void hikari_workspace_##name(struct hikari_workspace *workspace); + +WORKSPACE_ACTION(clear) +WORKSPACE_ACTION(quit_view) +WORKSPACE_ACTION(display_sheet_0) +WORKSPACE_ACTION(display_sheet_1) +WORKSPACE_ACTION(display_sheet_2) +WORKSPACE_ACTION(display_sheet_3) +WORKSPACE_ACTION(display_sheet_4) +WORKSPACE_ACTION(display_sheet_5) +WORKSPACE_ACTION(display_sheet_6) +WORKSPACE_ACTION(display_sheet_7) +WORKSPACE_ACTION(display_sheet_8) +WORKSPACE_ACTION(display_sheet_9) +WORKSPACE_ACTION(display_sheet_alternate) +WORKSPACE_ACTION(display_sheet_current) +WORKSPACE_ACTION(display_sheet_next) +WORKSPACE_ACTION(display_sheet_prev) +WORKSPACE_ACTION(switch_to_next_sheet) +WORKSPACE_ACTION(switch_to_prev_sheet) +WORKSPACE_ACTION(switch_to_next_inhabited_sheet) +WORKSPACE_ACTION(switch_to_prev_inhabited_sheet) +WORKSPACE_ACTION(raise_view) +WORKSPACE_ACTION(raise_group) +WORKSPACE_ACTION(lower_view) +WORKSPACE_ACTION(lower_group) +WORKSPACE_ACTION(hide_view) +WORKSPACE_ACTION(hide_group) +WORKSPACE_ACTION(snap_view_up) +WORKSPACE_ACTION(snap_view_down) +WORKSPACE_ACTION(snap_view_left) +WORKSPACE_ACTION(snap_view_right) +WORKSPACE_ACTION(only_view) +WORKSPACE_ACTION(only_group) +WORKSPACE_ACTION(pin_view_to_sheet_0) +WORKSPACE_ACTION(pin_view_to_sheet_1) +WORKSPACE_ACTION(pin_view_to_sheet_2) +WORKSPACE_ACTION(pin_view_to_sheet_3) +WORKSPACE_ACTION(pin_view_to_sheet_4) +WORKSPACE_ACTION(pin_view_to_sheet_5) +WORKSPACE_ACTION(pin_view_to_sheet_6) +WORKSPACE_ACTION(pin_view_to_sheet_7) +WORKSPACE_ACTION(pin_view_to_sheet_8) +WORKSPACE_ACTION(pin_view_to_sheet_9) +WORKSPACE_ACTION(pin_view_to_sheet_alternate) +WORKSPACE_ACTION(pin_view_to_sheet_current) +WORKSPACE_ACTION(pin_view_to_sheet_next) +WORKSPACE_ACTION(pin_view_to_sheet_prev) +WORKSPACE_ACTION(toggle_view_iconified) +WORKSPACE_ACTION(toggle_view_floating) +WORKSPACE_ACTION(toggle_view_full_maximize) +WORKSPACE_ACTION(toggle_view_vertical_maximize) +WORKSPACE_ACTION(toggle_view_horizontal_maximize) +WORKSPACE_ACTION(toggle_view_horizontal_maximize) +WORKSPACE_ACTION(reset_view_geometry) +WORKSPACE_ACTION(show_iconified_sheet_views) +WORKSPACE_ACTION(show_all_iconified_views) +WORKSPACE_ACTION(exchange_next_view) +WORKSPACE_ACTION(exchange_prev_view) +WORKSPACE_ACTION(exchange_main_layout_view) +WORKSPACE_ACTION(show_group) +WORKSPACE_ACTION(show_all_group_views) +WORKSPACE_ACTION(show_all_sheet_views) +#undef WORKSPACE_ACTION + +void +hikari_workspace_switch_sheet( + struct hikari_workspace *workspace, struct hikari_sheet *sheet); + +#endif diff --git a/include/hikari/xdg_view.h b/include/hikari/xdg_view.h new file mode 100644 index 0000000..fc22f97 --- /dev/null +++ b/include/hikari/xdg_view.h @@ -0,0 +1,43 @@ +#if !defined(HIKARI_XDG_VIEW_H) +#define HIKARI_XDG_VIEW_H + +#include + +#include + +#include + +struct hikari_render_data; + +struct hikari_xdg_view { + struct hikari_view view; + + struct wlr_xdg_surface *surface; + + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener commit; + struct wl_listener new_popup; + struct wl_listener new_subsurface; + struct wl_listener set_title; + struct wl_listener request_fullscreen; +}; + +void +hikari_xdg_view_init(struct hikari_xdg_view *xdg_view, + struct wlr_xdg_surface *xdg_surface, + struct hikari_workspace *workspace); + +struct hikari_xdg_popup { + struct hikari_view_child view_child; + + struct wlr_xdg_popup *popup; + + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener new_popup; +}; + +#endif diff --git a/include/hikari/xwayland_unmanaged_view.h b/include/hikari/xwayland_unmanaged_view.h new file mode 100644 index 0000000..c58b8d9 --- /dev/null +++ b/include/hikari/xwayland_unmanaged_view.h @@ -0,0 +1,39 @@ +#if !defined(HIKARI_XWAYLAND_UNMANAGED_VIEW_H) +#define HIKARI_XWAYLAND_UNMANAGED_VIEW_H + +#include +#include + +#include +#include + +#include + +struct hikari_workspace; + +struct hikari_xwayland_unmanaged_view { + struct hikari_view_interface view_interface; + + struct wlr_xwayland_surface *surface; + + struct hikari_workspace *workspace; + bool hidden; + + struct wlr_box geometry; + + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener request_configure; + struct wl_listener commit; + + struct wl_list unmanaged_server_views; +}; + +void +hikari_xwayland_unmanaged_view_init( + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view, + struct wlr_xwayland_surface *xwayland_surface, + struct hikari_workspace *workspace); + +#endif diff --git a/include/hikari/xwayland_view.h b/include/hikari/xwayland_view.h new file mode 100644 index 0000000..81525eb --- /dev/null +++ b/include/hikari/xwayland_view.h @@ -0,0 +1,28 @@ +#if !defined(HIKARI_XWAYLAND_VIEW_H) +#define HIKARI_XWAYLAND_VIEW_H + +#include + +#include + +struct hikari_render_data; + +struct hikari_xwayland_view { + struct hikari_view view; + + struct wlr_xwayland_surface *surface; + + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener request_configure; + struct wl_listener commit; + struct wl_listener set_title; +}; + +void +hikari_xwayland_view_init(struct hikari_xwayland_view *xwayland_view, + struct wlr_xwayland_surface *xwayland_surface, + struct hikari_workspace *workspace); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..8d07ef4 --- /dev/null +++ b/main.c @@ -0,0 +1,16 @@ +#include +#include + +#include + +int +main(int argc, char **argv) +{ +#ifndef NDEBUG + wlr_log_init(WLR_DEBUG, NULL); +#endif + hikari_server_start(); + hikari_server_stop(); + + return EXIT_SUCCESS; +} diff --git a/src/background.c b/src/background.c new file mode 100644 index 0000000..06ff4bd --- /dev/null +++ b/src/background.c @@ -0,0 +1,28 @@ +#include + +#include + +#include +#include + +#include + +void +hikari_background_init( + struct hikari_background *background, const char *output_name, char *path) +{ + size_t output_name_len = strlen(output_name); + background->output_name = hikari_malloc(output_name_len + 1); + + strcpy(background->output_name, output_name); + background->path = path; +} + +void +hikari_background_fini(struct hikari_background *background) +{ + assert(background != NULL); + + hikari_free(background->output_name); + hikari_free(background->path); +} diff --git a/src/border.c b/src/border.c new file mode 100644 index 0000000..79c9394 --- /dev/null +++ b/src/border.c @@ -0,0 +1,151 @@ +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +static void +rect_render(float color[static 4], + struct wlr_box *box, + struct hikari_render_data *render_data) +{ + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_union_rect( + &damage, &damage, box->x, box->y, box->width, box->height); + + pixman_region32_intersect(&damage, &damage, render_data->damage); + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto buffer_damage_finish; + } + + struct wlr_renderer *renderer = render_data->renderer; + assert(renderer); + + float matrix[9]; + wlr_matrix_project_box(matrix, + box, + WL_OUTPUT_TRANSFORM_NORMAL, + 0, + render_data->output->transform_matrix); + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; i++) { + hikari_output_scissor_render(render_data->output, renderer, &rects[i]); + wlr_render_quad_with_matrix(renderer, color, matrix); + } + +buffer_damage_finish: + pixman_region32_fini(&damage); +} + +void +hikari_border_render( + struct hikari_border *border, struct hikari_render_data *render_data) +{ + if (border->state == HIKARI_BORDER_NONE) { + return; + } + + struct wlr_box *geometry = &border->geometry; + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_union_rect(&damage, + &damage, + geometry->x, + geometry->y, + geometry->width, + geometry->height); + pixman_region32_intersect(&damage, &damage, render_data->damage); + + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto buffer_damage_finish; + } + + float *color; + switch (border->state) { + case HIKARI_BORDER_INACTIVE: + color = hikari_configuration.border_inactive; + break; + + case HIKARI_BORDER_ACTIVE: + color = hikari_configuration.border_active; + break; + + default: + goto buffer_damage_finish; + } + + struct wlr_renderer *renderer = render_data->renderer; + assert(renderer); + + float matrix[9]; + wlr_matrix_project_box(matrix, + geometry, + WL_OUTPUT_TRANSFORM_NORMAL, + 0, + render_data->output->transform_matrix); + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; i++) { + hikari_output_scissor_render(render_data->output, renderer, &rects[i]); + rect_render(color, &border->top, render_data); + rect_render(color, &border->bottom, render_data); + rect_render(color, &border->left, render_data); + rect_render(color, &border->right, render_data); + } + +buffer_damage_finish: + pixman_region32_fini(&damage); +} + +void +hikari_border_refresh_geometry( + struct hikari_border *border, struct wlr_box *geometry) +{ + if (border->state == HIKARI_BORDER_NONE) { + border->geometry = *geometry; + return; + } else { + border->geometry.x = geometry->x - hikari_configuration.border; + border->geometry.y = geometry->y - hikari_configuration.border; + border->geometry.width = geometry->width + hikari_configuration.border * 2; + border->geometry.height = + geometry->height + hikari_configuration.border * 2; + } + + border->top.x = border->geometry.x; + border->top.y = border->geometry.y; + border->top.width = border->geometry.width; + border->top.height = hikari_configuration.border; + + border->bottom.x = border->geometry.x; + border->bottom.y = border->geometry.y + border->geometry.height - + hikari_configuration.border; + border->bottom.width = border->geometry.width; + border->bottom.height = hikari_configuration.border; + + border->left.x = border->geometry.x; + border->left.y = border->geometry.y; + border->left.width = hikari_configuration.border; + border->left.height = border->geometry.height; + + border->right.x = + border->geometry.x + border->geometry.width - hikari_configuration.border; + border->right.y = border->geometry.y; + border->right.width = hikari_configuration.border; + border->right.height = border->geometry.height; +} diff --git a/src/command.c b/src/command.c new file mode 100644 index 0000000..35a3b7a --- /dev/null +++ b/src/command.c @@ -0,0 +1,32 @@ +#include + +#include +#include +#include +#include + +void +hikari_command_execute(const char *cmd) +{ + pid_t child; + int status; + + child = fork(); + if (child == 0) { + child = fork(); + if (child == 0) { + setsid(); + execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); + } + _exit(child == -1); + } + + for (;;) { + waitpid(child, &status, 0); + if (errno == EINTR) { + continue; + } else { + return; + } + } +} diff --git a/src/completion.c b/src/completion.c new file mode 100644 index 0000000..79668bc --- /dev/null +++ b/src/completion.c @@ -0,0 +1,83 @@ +#include + +#include +#include +#include + +#include + +void +hikari_completion_init(struct hikari_completion *completion, char *data) +{ + struct hikari_completion_item *item; + wl_list_init(&completion->items); + hikari_completion_add(completion, data); + item = wl_container_of(completion->items.next, item, completion_items); + completion->current_item = item; +} + +void +hikari_completion_fini(struct hikari_completion *completion) +{ + assert(completion != NULL); + + struct hikari_completion_item *item = NULL, *item_temp; + + wl_list_for_each_safe ( + item, item_temp, &completion->items, completion_items) { + wl_list_remove(&item->completion_items); + hikari_free(item); + } +} + +void +hikari_completion_add(struct hikari_completion *completion, char *data) +{ + assert(completion != NULL); + + struct hikari_completion_item *completion_item = + hikari_malloc(sizeof(struct hikari_completion_item)); + + strncpy(completion_item->data, data, sizeof(completion_item->data) - 1); + + wl_list_insert(completion->items.prev, &completion_item->completion_items); +} + +char * +hikari_completion_cancel(struct hikari_completion *completion) +{ + assert(completion != NULL); + assert(!wl_list_empty(&completion->items)); + + struct hikari_completion_item *item = + wl_container_of(completion->items.next, item, completion_items); + + completion->current_item = item; + + return item->data; +} + +#define COMPLETION(link) \ + char *hikari_completion_##link(struct hikari_completion *completion) \ + { \ + assert(completion != NULL); \ + assert(!wl_list_empty(&completion->items)); \ + \ + struct wl_list *link; \ + struct hikari_completion_item *item; \ + \ + link = completion->current_item->completion_items.link; \ + if (link == &completion->items) { \ + item = wl_container_of(completion->items.link, item, completion_items); \ + } else { \ + item = wl_container_of(link, item, completion_items); \ + } \ + \ + completion->current_item = item; \ + \ + return item->data; \ + } + +COMPLETION(next) +COMPLETION(prev) +#undef COMPLETION diff --git a/src/configuration.c b/src/configuration.c new file mode 100644 index 0000000..2bc7acc --- /dev/null +++ b/src/configuration.c @@ -0,0 +1,1427 @@ +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct hikari_configuration hikari_configuration; + +static bool +parse_modifier_mask(const char *str, uint8_t *result) +{ + size_t len = strlen(str); + uint8_t mask = 0; + int pos; + + for (pos = 0; pos < len; pos++) { + char c = str[pos]; + if (c == '-') { + break; + } else if (c == 'L') { + mask |= WLR_MODIFIER_LOGO; + } else if (c == 'S') { + mask |= WLR_MODIFIER_SHIFT; + } else if (c == 'A') { + mask |= WLR_MODIFIER_ALT; + } else if (c == 'C') { + mask |= WLR_MODIFIER_CTRL; + } else if (c == '5') { + mask |= WLR_MODIFIER_MOD5; + } else if (c == '0') { + // do nothing + } else { + fprintf(stderr, "config error: unkown modifier %c in %s\n", c, str); + return false; + } + } + + *result = mask; + return true; +} + +static struct hikari_split * +parse_container(const ucl_object_t *container_obj) +{ + const ucl_object_t *layout_type = ucl_object_lookup(container_obj, "type"); + + if (layout_type != NULL) { + layout_func_t layout_func = NULL; + const char *str = ucl_object_tostring(layout_type); + + if (!strcmp(str, "vertical")) { + layout_func = hikari_sheet_vertical_layout; + } else if (!strcmp(str, "horizontal")) { + layout_func = hikari_sheet_horizontal_layout; + } else if (!strcmp(str, "full")) { + layout_func = hikari_sheet_full_layout; + } else if (!strcmp(str, "grid")) { + layout_func = hikari_sheet_grid_layout; + } else { + assert(false); + } + + struct hikari_container *container = + hikari_malloc(sizeof(struct hikari_container)); + + // TODO fix upper bound + int views; + const ucl_object_t *views_obj = ucl_object_lookup(container_obj, "views"); + if (views_obj != NULL) { + views = ucl_object_toint(views_obj); + } else { + views = 1000; + } + + hikari_container_init(container, views, layout_func); + + return (struct hikari_split *)container; + } else { + return NULL; + } +} + +static struct hikari_split * +parse_vertical(const ucl_object_t *vertical_obj); + +static struct hikari_split * +parse_horizontal(const ucl_object_t *vertical_obj); + +static struct hikari_split * +parse_split(const ucl_object_t *split) +{ + struct hikari_split *ret; + ucl_object_iter_t it = ucl_object_iterate_new(split); + + const ucl_object_t *cur; + while ((cur = ucl_object_iterate_safe(it, true)) != NULL) { + const char *key = ucl_object_key(cur); + if (!strcmp(key, "container")) { + ret = parse_container(cur); + } else if (!strcmp(key, "vertical")) { + ret = parse_vertical(cur); + } else if (!strcmp(key, "horizontal")) { + ret = parse_horizontal(cur); + } else { + assert(false); + } + } + + ucl_object_iterate_free(it); + + return ret; +} + +static struct hikari_split * +parse_vertical(const ucl_object_t *vertical_obj) +{ + const ucl_object_t *ratio_obj = ucl_object_lookup(vertical_obj, "ratio"); + const ucl_object_t *right_obj = ucl_object_lookup(vertical_obj, "right"); + const ucl_object_t *left_obj = ucl_object_lookup(vertical_obj, "left"); + + double ratio; + + if (ratio_obj != NULL) { + ratio = ucl_object_todouble(ratio_obj); + } else { + ratio = 0.5; + } + + struct hikari_split *right; + if (right_obj != NULL) { + right = parse_split(right_obj); + } else { + right = NULL; + assert(false); + } + + struct hikari_split *left; + if (left_obj != NULL) { + left = parse_split(left_obj); + } else { + left = NULL; + assert(false); + } + + struct hikari_vertical_split *vertical = + hikari_malloc(sizeof(struct hikari_vertical_split)); + + hikari_vertical_split_init(vertical, ratio, left, right); + + return (struct hikari_split *)vertical; +} + +static struct hikari_split * +parse_horizontal(const ucl_object_t *horizontal_obj) +{ + const ucl_object_t *ratio_obj = ucl_object_lookup(horizontal_obj, "ratio"); + const ucl_object_t *top_obj = ucl_object_lookup(horizontal_obj, "top"); + const ucl_object_t *bottom_obj = ucl_object_lookup(horizontal_obj, "bottom"); + + double ratio; + + if (ratio_obj != NULL) { + ratio = ucl_object_todouble(ratio_obj); + } else { + ratio = 0.5; + } + + struct hikari_split *top; + if (top_obj != NULL) { + top = parse_split(top_obj); + } else { + top = NULL; + assert(false); + } + + struct hikari_split *bottom; + if (bottom_obj != NULL) { + bottom = parse_split(bottom_obj); + } else { + bottom = NULL; + assert(false); + } + + struct hikari_horizontal_split *horizontal = + hikari_malloc(sizeof(struct hikari_horizontal_split)); + + hikari_horizontal_split_init(horizontal, ratio, top, bottom); + + return (struct hikari_split *)horizontal; +} + +static struct hikari_view_autoconf * +resolve_autoconf(struct hikari_configuration *configuration, const char *app_id) +{ + if (app_id != NULL) { + struct hikari_view_autoconf *autoconf; + wl_list_for_each (autoconf, &hikari_configuration.autoconfs, link) { + if (!strcmp(autoconf->app_id, app_id)) { + return autoconf; + } + } + } + + return NULL; +} + +void +hikari_configuration_resolve_view_autoconf( + struct hikari_configuration *configuration, + const char *app_id, + struct hikari_view *view, + struct hikari_sheet **sheet, + struct hikari_group **group, + int *x, + int *y, + bool *focus) +{ + struct hikari_view_autoconf *view_autoconf = + resolve_autoconf(&hikari_configuration, app_id); + + if (view_autoconf != NULL) { + *focus = view_autoconf->focus; + + if (view_autoconf->sheet_nr != -1) { + *sheet = hikari_server.workspace->sheets + view_autoconf->sheet_nr; + } else { + *sheet = hikari_server.workspace->sheet; + } + + if (strlen(view_autoconf->group_name) > 0) { + *group = hikari_server_find_or_create_group(view_autoconf->group_name); + } else { + *group = (*sheet)->group; + } + + if (view_autoconf->position.x != -1 && view_autoconf->position.y != -1) { + *x = view_autoconf->position.x; + *y = view_autoconf->position.y; + } else { + struct hikari_output *output = hikari_server.workspace->output; + + *x = hikari_server.cursor->x - output->geometry.x; + *y = hikari_server.cursor->y - output->geometry.y; + } + + if (view_autoconf->mark != NULL && view_autoconf->mark->view == NULL) { + hikari_mark_set(view_autoconf->mark, view); + } + } else { + struct hikari_output *output = hikari_server.workspace->output; + + *sheet = hikari_server.workspace->sheet; + *group = (*sheet)->group; + + *x = hikari_server.cursor->x - output->geometry.x; + *y = hikari_server.cursor->y - output->geometry.y; + } +} + +static char * +copy_in_config_string(const ucl_object_t *obj) +{ + const char *str; + char *ret; + + bool success = ucl_object_tostring_safe(obj, &str); + + if (success) { + size_t len = strlen(str); + ret = hikari_malloc(len + 1); + strcpy(ret, str); + + return ret; + } else { + return NULL; + } +} + +static void +parse_colorscheme( + struct hikari_configuration *configuration, const ucl_object_t *obj) +{ + ucl_object_iter_t it = ucl_object_iterate_new(obj); + + const ucl_object_t *cur; + int64_t color; + while ((cur = ucl_object_iterate_safe(it, false)) != NULL) { + const char *key = ucl_object_key(cur); + + if (!strcmp("indicator_selected", key)) { + ucl_object_toint_safe(cur, &color); + hikari_color_convert(configuration->indicator_selected, color); + } else if (!strcmp("indicator_grouped", key)) { + ucl_object_toint_safe(cur, &color); + hikari_color_convert(configuration->indicator_grouped, color); + } else if (!strcmp("indicator_first", key)) { + ucl_object_toint_safe(cur, &color); + hikari_color_convert(configuration->indicator_first, color); + } else if (!strcmp("indicator_conflict", key)) { + ucl_object_toint_safe(cur, &color); + hikari_color_convert(configuration->indicator_conflict, color); + } else if (!strcmp("indicator_insert", key)) { + ucl_object_toint_safe(cur, &color); + hikari_color_convert(configuration->indicator_insert, color); + } else if (!strcmp("border_active", key)) { + ucl_object_toint_safe(cur, &color); + hikari_color_convert(configuration->border_active, color); + } else if (!strcmp("border_inactive", key)) { + ucl_object_toint_safe(cur, &color); + hikari_color_convert(configuration->border_inactive, color); + } else if (!strcmp("foreground", key)) { + ucl_object_toint_safe(cur, &color); + hikari_color_convert(configuration->foreground, color); + } else if (!strcmp("background", key)) { + ucl_object_toint_safe(cur, &color); + hikari_color_convert(configuration->clear, color); + } + } + + ucl_object_iterate_free(it); +} + +static bool +parse_execute( + struct hikari_configuration *configuration, const ucl_object_t *obj) +{ + bool success = true; + const ucl_object_t *cur; + const char *key; + + ucl_object_iter_t it = ucl_object_iterate_new(obj); + while ((cur = ucl_object_iterate_safe(it, false)) != NULL) { + key = ucl_object_key(cur); + + struct hikari_exec *execute = NULL; + + if (!strcmp("a", key)) { + execute = HIKARI_EXEC_a; + } else if (!strcmp("b", key)) { + execute = HIKARI_EXEC_b; + } else if (!strcmp("c", key)) { + execute = HIKARI_EXEC_c; + } else if (!strcmp("d", key)) { + execute = HIKARI_EXEC_d; + } else if (!strcmp("e", key)) { + execute = HIKARI_EXEC_e; + } else if (!strcmp("f", key)) { + execute = HIKARI_EXEC_f; + } else if (!strcmp("g", key)) { + execute = HIKARI_EXEC_g; + } else if (!strcmp("h", key)) { + execute = HIKARI_EXEC_h; + } else if (!strcmp("i", key)) { + execute = HIKARI_EXEC_i; + } else if (!strcmp("j", key)) { + execute = HIKARI_EXEC_j; + } else if (!strcmp("k", key)) { + execute = HIKARI_EXEC_k; + } else if (!strcmp("l", key)) { + execute = HIKARI_EXEC_l; + } else if (!strcmp("m", key)) { + execute = HIKARI_EXEC_m; + } else if (!strcmp("n", key)) { + execute = HIKARI_EXEC_n; + } else if (!strcmp("o", key)) { + execute = HIKARI_EXEC_o; + } else if (!strcmp("p", key)) { + execute = HIKARI_EXEC_p; + } else if (!strcmp("q", key)) { + execute = HIKARI_EXEC_q; + } else if (!strcmp("r", key)) { + execute = HIKARI_EXEC_r; + } else if (!strcmp("s", key)) { + execute = HIKARI_EXEC_s; + } else if (!strcmp("t", key)) { + execute = HIKARI_EXEC_t; + } else if (!strcmp("u", key)) { + execute = HIKARI_EXEC_u; + } else if (!strcmp("v", key)) { + execute = HIKARI_EXEC_v; + } else if (!strcmp("w", key)) { + execute = HIKARI_EXEC_w; + } else if (!strcmp("x", key)) { + execute = HIKARI_EXEC_x; + } else if (!strcmp("y", key)) { + execute = HIKARI_EXEC_y; + } else if (!strcmp("z", key)) { + execute = HIKARI_EXEC_z; + } + + if (execute != NULL) { + execute->command = copy_in_config_string(cur); + execute = NULL; + } else { + fprintf(stderr, "config error: invalid exec register %s\n", key); + success = false; + break; + } + } + ucl_object_iterate_free(it); + + if (!success) { + for (int i = 0; i < HIKARI_NR_OF_MARKS; i++) { + hikari_free(execs[i].command); + } + } + + return success; +} + +static bool +parse_autoconf( + struct hikari_configuration *configuration, const ucl_object_t *obj) +{ + ucl_object_iter_t it = ucl_object_iterate_new(obj); + + const ucl_object_t *cur; + while ((cur = ucl_object_iterate_safe(it, false)) != NULL) { + struct hikari_view_autoconf *autoconf = + hikari_malloc(sizeof(struct hikari_view_autoconf)); + + const char *key = ucl_object_key(cur); + size_t keylen = strlen(key); + + autoconf->app_id = hikari_malloc(keylen + 1); + strcpy(autoconf->app_id, key); + + const ucl_object_t *group = ucl_object_lookup(cur, "group"); + if (group != NULL) { + autoconf->group_name = copy_in_config_string(group); + } else { + autoconf->group_name = NULL; + } + + const ucl_object_t *sheet = ucl_object_lookup(cur, "sheet"); + if (sheet != NULL) { + autoconf->sheet_nr = ucl_object_toint(sheet); + } else { + autoconf->sheet_nr = -1; + } + + const ucl_object_t *mark = ucl_object_lookup(cur, "mark"); + if (mark != NULL) { + char mark_name = ucl_object_tostring(mark)[0]; + autoconf->mark = &marks[mark_name - 'a']; + } else { + autoconf->mark = NULL; + } + + const ucl_object_t *position = ucl_object_lookup(cur, "position"); + if (position != NULL) { + const ucl_object_t *x = ucl_object_lookup(position, "x"); + const ucl_object_t *y = ucl_object_lookup(position, "y"); + autoconf->position.x = ucl_object_toint(x); + autoconf->position.y = ucl_object_toint(y); + } else { + autoconf->position.x = -1; + autoconf->position.y = -1; + } + + const ucl_object_t *focus = ucl_object_lookup(cur, "focus"); + if (focus != NULL) { + autoconf->focus = ucl_object_toboolean(focus); + } else { + autoconf->focus = false; + } + + wl_list_insert(&hikari_configuration.autoconfs, &autoconf->link); + } + + ucl_object_iterate_free(it); + + return true; +} + +static bool +parse_keycode(const char *str, uint32_t *keycode, uint32_t *modifiers) +{ + uint32_t mods = 0; + uint32_t code = 0; + + size_t len = strlen(str); + int pos; + + for (pos = 0; pos < len; pos++) { + char c = str[pos]; + if (c == '-') { + break; + } else if (c == 'L') { + mods |= WLR_MODIFIER_LOGO; + } else if (c == 'S') { + mods |= WLR_MODIFIER_SHIFT; + } else if (c == 'A') { + mods |= WLR_MODIFIER_ALT; + } else if (c == 'C') { + mods |= WLR_MODIFIER_CTRL; + } else if (c == '5') { + mods |= WLR_MODIFIER_MOD5; + } else if (c == '0') { + // do nothing + } else { + fprintf(stderr, "config error: could not parse keycode\n"); + return false; + } + } + + pos++; + + code = atoi(str + pos); + + *keycode = code; + *modifiers = mods; + return true; +} + +static char * +lookup_action(const char *action, const ucl_object_t *actions) +{ + const ucl_object_t *obj = ucl_object_lookup(actions, action + 7); + + if (obj != NULL) { + return copy_in_config_string(obj); + } else { + return NULL; + } +} + +static struct hikari_split * +lookup_layout(const char *layout, const ucl_object_t *layouts) +{ + const ucl_object_t *obj = ucl_object_lookup(layouts, layout + 7); + + if (obj != NULL) { + return parse_split(obj); + } else { + return NULL; + } +} + +static void +cleanup_execute(void *arg) +{ + char *command = arg; + hikari_free(command); +} + +static void +cleanup_layout(void *arg) +{ + struct hikari_split *split = arg; + hikari_split_fini(split); +} + +#ifndef NDEBUG +static void +toggle_damage_tracking(void *arg) +{ + hikari_server.track_damage = !hikari_server.track_damage; + + struct hikari_output *output = NULL; + wl_list_for_each (output, &hikari_server.outputs, server_outputs) { + hikari_output_damage_whole(output); + } +} +#endif + +static bool +parse_binding(const ucl_object_t *obj, + const ucl_object_t *actions, + const ucl_object_t *layouts, + void (**action)(void *), + void (**cleanup)(void *), + void **arg) +{ + const char *str; + bool success = ucl_object_tostring_safe(obj, &str); + + if (success) { + if (!strcmp(str, "quit")) { + *action = hikari_server_terminate; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "lock")) { + *action = hikari_server_lock; + *cleanup = NULL; + *arg = NULL; +#ifndef NDEBUG + } else if (!strcmp(str, "debug-damage")) { + *action = toggle_damage_tracking; + *cleanup = NULL; + *arg = NULL; +#endif + + } else if (!strcmp(str, "view-move-up")) { + *action = hikari_server_move_view_up; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-move-down")) { + *action = hikari_server_move_view_down; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-move-left")) { + *action = hikari_server_move_view_left; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-move-right")) { + *action = hikari_server_move_view_right; + *cleanup = NULL; + *arg = NULL; + + } else if (!strcmp(str, "view-snap-up")) { + *action = hikari_server_snap_view_up; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-snap-down")) { + *action = hikari_server_snap_view_down; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-snap-left")) { + *action = hikari_server_snap_view_left; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-snap-right")) { + *action = hikari_server_snap_view_right; + *cleanup = NULL; + *arg = NULL; + + } else if (!strcmp(str, "view-toggle-maximize-vertical")) { + *action = hikari_server_toggle_view_vertical_maximize; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-toggle-maximize-horizontal")) { + *action = hikari_server_toggle_view_horizontal_maximize; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-toggle-maximize-full")) { + *action = hikari_server_toggle_view_full_maximize; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-toggle-floating")) { + *action = hikari_server_toggle_view_floating; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-toggle-iconified")) { + *action = hikari_server_toggle_view_iconified; + *cleanup = NULL; + *arg = NULL; + + } else if (!strcmp(str, "view-raise")) { + *action = hikari_server_raise_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-lower")) { + *action = hikari_server_lower_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-hide")) { + *action = hikari_server_hide_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-only")) { + *action = hikari_server_only_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-quit")) { + *action = hikari_server_quit_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-reset-geometry")) { + *action = hikari_server_reset_view_geometry; + *cleanup = NULL; + *arg = NULL; + + } else if (!strcmp(str, "view-pin-to-sheet-0")) { + *action = hikari_server_pin_view_to_sheet_0; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-1")) { + *action = hikari_server_pin_view_to_sheet_1; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-2")) { + *action = hikari_server_pin_view_to_sheet_2; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-3")) { + *action = hikari_server_pin_view_to_sheet_3; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-4")) { + *action = hikari_server_pin_view_to_sheet_4; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-5")) { + *action = hikari_server_pin_view_to_sheet_5; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-6")) { + *action = hikari_server_pin_view_to_sheet_6; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-7")) { + *action = hikari_server_pin_view_to_sheet_7; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-8")) { + *action = hikari_server_pin_view_to_sheet_8; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-9")) { + *action = hikari_server_pin_view_to_sheet_9; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-alternate")) { + *action = hikari_server_pin_view_to_sheet_alternate; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-current")) { + *action = hikari_server_pin_view_to_sheet_current; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-next")) { + *action = hikari_server_pin_view_to_sheet_next; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-pin-to-sheet-prev")) { + *action = hikari_server_pin_view_to_sheet_prev; + *cleanup = NULL; + *arg = NULL; + + } else if (!strcmp(str, "view-decrease-size-up")) { + *action = hikari_server_decrease_view_size_up; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-increase-size-down")) { + *action = hikari_server_increase_view_size_down; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-decrease-size-left")) { + *action = hikari_server_decrease_view_size_left; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "view-increase-size-right")) { + *action = hikari_server_increase_view_size_right; + *cleanup = NULL; + *arg = NULL; + + } else if (!strcmp(str, "sheet-switch-to-0")) { + *action = hikari_server_display_sheet_0; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-1")) { + *action = hikari_server_display_sheet_1; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-2")) { + *action = hikari_server_display_sheet_2; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-3")) { + *action = hikari_server_display_sheet_3; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-4")) { + *action = hikari_server_display_sheet_4; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-5")) { + *action = hikari_server_display_sheet_5; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-6")) { + *action = hikari_server_display_sheet_6; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-7")) { + *action = hikari_server_display_sheet_7; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-8")) { + *action = hikari_server_display_sheet_8; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-9")) { + *action = hikari_server_display_sheet_9; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-alternate")) { + *action = hikari_server_display_sheet_alternate; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-current")) { + *action = hikari_server_display_sheet_current; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-next")) { + *action = hikari_server_display_sheet_next; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-prev")) { + *action = hikari_server_display_sheet_prev; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-next-inhabited")) { + *action = hikari_server_switch_to_next_inhabited_sheet; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-switch-to-prev-inhabited")) { + *action = hikari_server_switch_to_prev_inhabited_sheet; + *cleanup = NULL; + *arg = NULL; + + } else if (!strcmp(str, "sheet-show-iconified")) { + *action = hikari_server_show_iconified_sheet_views; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-show-all")) { + *action = hikari_server_show_all_sheet_views; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-cycle-view-next")) { + *action = hikari_server_cycle_next_sheet_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-cycle-view-prev")) { + *action = hikari_server_cycle_prev_sheet_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-reset-layout")) { + *action = hikari_server_reset_sheet_layout; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-cycle-layout-view-next")) { + *action = hikari_server_cycle_next_layout_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-cycle-layout-view-prev")) { + *action = hikari_server_cycle_prev_layout_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-cycle-layout-view-first")) { + *action = hikari_server_cycle_first_layout_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-cycle-layout-view-last")) { + *action = hikari_server_cycle_last_layout_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-exchange-layout-view-next")) { + *action = hikari_server_exchange_next_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-exchange-layout-view-prev")) { + *action = hikari_server_exchange_prev_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "sheet-exchange-layout-view-main")) { + *action = hikari_server_exchange_main_layout_view; + *cleanup = NULL; + *arg = NULL; + + } else if (!strcmp(str, "mode-enter-execute")) { + *action = hikari_server_enter_exec_select_mode; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "mode-enter-group-assign")) { + *action = hikari_server_enter_group_assign_mode; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "mode-enter-keyboard-grab")) { + *action = hikari_server_enter_keyboard_grab_mode; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "mode-enter-mark-assign")) { + *action = hikari_server_enter_mark_assign_mode; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "mode-enter-mark-select")) { + *action = hikari_server_enter_mark_select_mode; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "mode-enter-mark-switch-select")) { + *action = hikari_server_enter_mark_select_switch_mode; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "mode-enter-move")) { + *action = hikari_server_enter_move_mode; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "mode-enter-resize")) { + *action = hikari_server_enter_resize_mode; + *cleanup = NULL; + *arg = NULL; + + } else if (!strcmp(str, "group-only")) { + *action = hikari_server_only_group; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "group-hide")) { + *action = hikari_server_hide_group; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "group-raise")) { + *action = hikari_server_raise_group; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "group-lower")) { + *action = hikari_server_lower_group; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "group-cycle-next")) { + *action = hikari_server_cycle_next_group; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "group-cycle-prev")) { + *action = hikari_server_cycle_prev_group; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "group-cycle-view-next")) { + *action = hikari_server_cycle_next_group_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "group-cycle-view-prev")) { + *action = hikari_server_cycle_prev_group_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "group-cycle-view-first")) { + *action = hikari_server_cycle_first_group_view; + *cleanup = NULL; + *arg = NULL; + } else if (!strcmp(str, "group-cycle-view-last")) { + *action = hikari_server_cycle_last_group_view; + *cleanup = NULL; + *arg = NULL; + + } else { + char *command = lookup_action(str, actions); + struct hikari_split *layout = lookup_layout(str, layouts); + if (command != NULL) { + *action = hikari_server_execute_command; + *cleanup = cleanup_execute; + *arg = command; + } else if (layout != NULL) { + *action = hikari_server_layout_sheet; + *cleanup = cleanup_layout; + *arg = layout; + } else { + fprintf(stderr, "config error: unknown action %s\n", str); + assert(false); + success = false; + } + } + } + + return success; +} + +static bool +parse_keyboard_bindings(struct hikari_configuration *configuration, + const ucl_object_t *actions, + const ucl_object_t *layouts, + uint8_t *nbindings, + struct hikari_keybinding **bindings, + const ucl_object_t *bindings_keycode) +{ + int n[256] = { 0 }; + uint8_t mask = 0; + const ucl_object_t *cur; + bool success = true; + + ucl_object_iter_t it = ucl_object_iterate_new(bindings_keycode); + while ((cur = ucl_object_iterate_safe(it, false)) != NULL) { + const char *key = ucl_object_key(cur); + + if (!parse_modifier_mask(key, &mask)) { + success = false; + goto done; + } + // TODO check for overflow + n[mask]++; + } + ucl_object_iterate_free(it); + + for (int i = 0; i < 256; i++) { + if (n[i] > 0) { + nbindings[i] = n[i]; + bindings[i] = hikari_calloc(n[i], sizeof(struct hikari_keybinding)); + } else { + bindings[i] = NULL; + } + + n[i] = 0; + } + + it = ucl_object_iterate_new(bindings_keycode); + while ((cur = ucl_object_iterate_safe(it, true)) != NULL) { + const char *key = ucl_object_key(cur); + + parse_modifier_mask(key, &mask); + + struct hikari_keybinding *binding = &bindings[mask][n[mask]]; + + if (!parse_keycode(key, &binding->keycode, &binding->modifiers) || + !parse_binding(cur, + actions, + layouts, + &binding->action, + &binding->cleanup, + &binding->arg)) { + success = false; + // TODO cleanup + goto done; + } + n[mask]++; + } + +done: + ucl_object_iterate_free(it); + + return success; +} + +static bool +parse_bindings(struct hikari_configuration *configuration, + const ucl_object_t *actions, + const ucl_object_t *layouts, + const ucl_object_t *bindings_obj) +{ + const ucl_object_t *keyboard_obj = + ucl_object_lookup(bindings_obj, "keyboard"); + + if (keyboard_obj != NULL) { + parse_keyboard_bindings(configuration, + actions, + layouts, + configuration->normal_mode.nkeybindings, + configuration->normal_mode.keybindings, + keyboard_obj); + } + + const ucl_object_t *mouse_obj = ucl_object_lookup(bindings_obj, "mouse"); + + if (mouse_obj != NULL) { + parse_keyboard_bindings(configuration, + actions, + layouts, + configuration->normal_mode.nmousebindings, + configuration->normal_mode.mousebindings, + mouse_obj); + } + + return true; +} + +static bool +parse_pointer_config(struct hikari_configuration *configuration, + const ucl_object_t *pointer_config_obj) +{ + const char *key = ucl_object_key(pointer_config_obj); + size_t keylen = strlen(key); + + struct hikari_pointer_config *pointer_config = + hikari_malloc(sizeof(struct hikari_pointer_config)); + + pointer_config->name = hikari_malloc(keylen + 1); + strcpy(pointer_config->name, key); + + const ucl_object_t *accel_obj = + ucl_object_lookup(pointer_config_obj, "accel"); + + if (accel_obj != NULL) { + pointer_config->accel = ucl_object_todouble(accel_obj); + } else { + pointer_config->accel = 0; + } + + const ucl_object_t *scroll_button_obj = + ucl_object_lookup(pointer_config_obj, "scroll-button"); + + if (scroll_button_obj != NULL) { + pointer_config->scroll_button = ucl_object_toint(scroll_button_obj); + } else { + pointer_config->scroll_button = 0; + } + + const ucl_object_t *scroll_method_obj = + ucl_object_lookup(pointer_config_obj, "scroll-method"); + + if (scroll_method_obj != NULL) { + const char *method = ucl_object_tostring(scroll_method_obj); + + if (!strcmp(method, "on-button-down")) { + pointer_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; + } else { + pointer_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; + } + + } else { + pointer_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; + } + + wl_list_insert(&configuration->pointer_configs, &pointer_config->link); + + return true; +} + +static bool +parse_pointers( + struct hikari_configuration *configuration, const ucl_object_t *inputs_obj) +{ + ucl_object_iter_t it = ucl_object_iterate_new(inputs_obj); + + const ucl_object_t *cur; + while ((cur = ucl_object_iterate_safe(it, true)) != NULL) { + parse_pointer_config(configuration, cur); + } + + ucl_object_iterate_free(it); + + return true; +} + +static bool +parse_inputs( + struct hikari_configuration *configuration, const ucl_object_t *inputs_obj) +{ + const ucl_object_t *pointers_obj = ucl_object_lookup(inputs_obj, "pointers"); + + if (pointers_obj != NULL) { + parse_pointers(configuration, pointers_obj); + } + + return true; +} + +static bool +parse_background(struct hikari_configuration *configuration, + const ucl_object_t *background_obj) +{ + const char *key = ucl_object_key(background_obj); + char *background_path = copy_in_config_string(background_obj); + struct hikari_background *background = + hikari_malloc(sizeof(struct hikari_background)); + + if (background_path != NULL) { + hikari_background_init(background, key, background_path); + + wl_list_insert(&configuration->backgrounds, &background->link); + return true; + } else { + return false; + } +} + +static bool +parse_backgrounds(struct hikari_configuration *configuration, + const ucl_object_t *backgrounds_obj) +{ + bool success = true; + ucl_object_iter_t it = ucl_object_iterate_new(backgrounds_obj); + + const ucl_object_t *cur; + while ((cur = ucl_object_iterate_safe(it, true)) != NULL) { + if (!parse_background(configuration, cur)) { + success = false; + } + } + ucl_object_iterate_free(it); + + return success; +} + +static void +parse_border( + struct hikari_configuration *configuration, const ucl_object_t *border_obj) +{ + int64_t border = 1; + + if (!ucl_object_toint_safe(border_obj, &border)) { + assert(false); + } + + configuration->border = border; +} + +static void +parse_gap( + struct hikari_configuration *configuration, const ucl_object_t *gap_obj) +{ + int64_t gap = 5; + + if (!ucl_object_toint_safe(gap_obj, &gap)) { + assert(false); + } + + configuration->gap = gap; +} + +static void +parse_font( + struct hikari_configuration *configuration, const ucl_object_t *font_obj) +{ + const char *font = ucl_object_tostring(font_obj); + + hikari_font_init(&configuration->font, font); +} + +static char * +get_config_path(void) +{ + char *config_home = getenv("HOME"); + char *path = "/.config/hikari/hikari.conf"; + size_t len = strlen(config_home) + strlen(path); + + char *ret = malloc(len + 1); + + memset(ret, 0, len + 1); + + strcat(ret, config_home); + strcat(ret, path); + + return ret; +} + +static void +parse_configuration(struct hikari_configuration *configuration) +{ + struct ucl_parser *parser = ucl_parser_new(0); + char *config_path = get_config_path(); + bool success = true; + + ucl_parser_add_file(parser, config_path); + ucl_object_t *obj = ucl_parser_get_object(parser); + + const ucl_object_t *colorscheme = ucl_object_lookup(obj, "colorscheme"); + const ucl_object_t *actions = ucl_object_lookup(obj, "actions"); + const ucl_object_t *layouts = ucl_object_lookup(obj, "layouts"); + const ucl_object_t *autoconf = ucl_object_lookup(obj, "autoconf"); + const ucl_object_t *execute = ucl_object_lookup(obj, "execute"); + const ucl_object_t *bindings = ucl_object_lookup(obj, "bindings"); + const ucl_object_t *backgrounds = ucl_object_lookup(obj, "backgrounds"); + const ucl_object_t *font = ucl_object_lookup(obj, "font"); + const ucl_object_t *border = ucl_object_lookup(obj, "border"); + const ucl_object_t *gap = ucl_object_lookup(obj, "gap"); + const ucl_object_t *inputs = ucl_object_lookup(obj, "inputs"); + + if (colorscheme != NULL) { + parse_colorscheme(configuration, colorscheme); + } else { + hikari_color_convert(configuration->clear, 0x282C34); + hikari_color_convert(configuration->foreground, 0x000000); + hikari_color_convert(configuration->indicator_selected, 0xE6DB74); + hikari_color_convert(configuration->indicator_grouped, 0xFD971F); + hikari_color_convert(configuration->indicator_first, 0xB8E673); + hikari_color_convert(configuration->indicator_conflict, 0xEF5939); + hikari_color_convert(configuration->indicator_insert, 0x66D9EF); + hikari_color_convert(configuration->border_active, 0xFFFFFF); + hikari_color_convert(configuration->border_inactive, 0x465457); + } + + if (autoconf != NULL && !parse_autoconf(configuration, autoconf)) { + success = false; + goto done; + } + + if (execute != NULL && !parse_execute(configuration, execute)) { + success = false; + goto done; + } + + if (bindings != NULL && + !parse_bindings(configuration, actions, layouts, bindings)) { + success = false; + goto done; + } + + if (backgrounds != NULL && !parse_backgrounds(configuration, backgrounds)) { + success = false; + goto done; + } + + if (inputs != NULL && !parse_inputs(configuration, inputs)) { + success = false; + goto done; + } + + if (font != NULL) { + parse_font(configuration, font); + } + + if (border != NULL) { + parse_border(configuration, border); + } else { + configuration->border = 1; + } + + if (gap != NULL) { + parse_gap(configuration, gap); + } else { + configuration->gap = 5; + } + +done: + + ucl_object_unref(obj); + ucl_parser_free(parser); + + free(config_path); + + if (!success) { + exit(EXIT_FAILURE); + } +} + +void +hikari_configuration_init(struct hikari_configuration *configuration) +{ + wl_list_init(&configuration->autoconfs); + wl_list_init(&configuration->backgrounds); + wl_list_init(&configuration->pointer_configs); + + return parse_configuration(configuration); +} + +static void +cleanup_bindings(struct hikari_keybinding **bindings, uint8_t *n) +{ + for (int i = 0; i < 256; i++) { + struct hikari_keybinding *mod_bindings = bindings[i]; + uint8_t nbindings = n[i]; + + for (uint8_t j = 0; j < nbindings; j++) { + struct hikari_keybinding *binding = &mod_bindings[j]; + if (binding != NULL) { + hikari_keybinding_fini(binding); + } + } + hikari_free(mod_bindings); + } +} + +void +hikari_configuration_fini(struct hikari_configuration *configuration) +{ + cleanup_bindings(configuration->normal_mode.keybindings, + configuration->normal_mode.nkeybindings); + + cleanup_bindings(configuration->normal_mode.mousebindings, + configuration->normal_mode.nmousebindings); + + struct hikari_view_autoconf *autoconf, *autoconf_temp; + wl_list_for_each_safe ( + autoconf, autoconf_temp, &configuration->autoconfs, link) { + wl_list_remove(&autoconf->link); + + hikari_view_autoconf_fini(autoconf); + hikari_free(autoconf); + } + + struct hikari_background *background, *background_temp; + wl_list_for_each_safe ( + background, background_temp, &configuration->backgrounds, link) { + wl_list_remove(&background->link); + + hikari_background_fini(background); + hikari_free(background); + } + + struct hikari_pointer_config *pointer_config, *pointer_config_temp; + wl_list_for_each_safe (pointer_config, + pointer_config_temp, + &configuration->pointer_configs, + link) { + wl_list_remove(&pointer_config->link); + + hikari_pointer_config_fini(pointer_config); + hikari_free(pointer_config); + } +} + +char * +hikari_configuration_resolve_background( + struct hikari_configuration *configuration, const char *output_name) +{ + struct hikari_background *background; + wl_list_for_each (background, &hikari_configuration.backgrounds, link) { + if (!strcmp(background->output_name, output_name)) { + return background->path; + } + } + + return NULL; +} + +struct hikari_pointer_config * +hikari_configuration_resolve_pointer_config( + struct hikari_configuration *configuration, const char *pointer_name) +{ + + struct hikari_pointer_config *pointer_config; + wl_list_for_each ( + pointer_config, &hikari_configuration.pointer_configs, link) { + if (!strcmp(pointer_config->name, pointer_name)) { + return pointer_config; + } + } + + return NULL; +} diff --git a/src/exec.c b/src/exec.c new file mode 100644 index 0000000..0e37f98 --- /dev/null +++ b/src/exec.c @@ -0,0 +1,26 @@ +#include + +#include +#include +#include + +#include +#include + +struct hikari_exec execs[HIKARI_NR_OF_EXECS]; + +void +hikari_execs_init(void) +{ + /* for (int i = 0; i < HIKARI_NR_OF_EXECS; i++) { */ + /* execs[i].command = NULL; */ + /* } */ +} + +void +hikari_execs_fini(void) +{ + for (int i = 0; i < HIKARI_NR_OF_EXECS; i++) { + hikari_free(execs[i].command); + } +} diff --git a/src/exec_select_mode.c b/src/exec_select_mode.c new file mode 100644 index 0000000..a8b20e9 --- /dev/null +++ b/src/exec_select_mode.c @@ -0,0 +1,230 @@ +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct hikari_exec * +lookup_exec(struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard, + bool *selected) +{ + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + *selected = false; + struct hikari_exec *exec = NULL; + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_a: + exec = HIKARI_EXEC_a; + *selected = true; + break; + + case XKB_KEY_b: + exec = HIKARI_EXEC_b; + *selected = true; + break; + + case XKB_KEY_c: + exec = HIKARI_EXEC_c; + *selected = true; + break; + + case XKB_KEY_d: + exec = HIKARI_EXEC_d; + *selected = true; + break; + + case XKB_KEY_e: + exec = HIKARI_EXEC_e; + *selected = true; + break; + + case XKB_KEY_f: + exec = HIKARI_EXEC_f; + *selected = true; + break; + + case XKB_KEY_g: + exec = HIKARI_EXEC_g; + *selected = true; + break; + + case XKB_KEY_h: + exec = HIKARI_EXEC_h; + *selected = true; + break; + + case XKB_KEY_i: + exec = HIKARI_EXEC_i; + *selected = true; + break; + + case XKB_KEY_j: + exec = HIKARI_EXEC_j; + *selected = true; + break; + + case XKB_KEY_k: + exec = HIKARI_EXEC_k; + *selected = true; + break; + + case XKB_KEY_l: + exec = HIKARI_EXEC_l; + *selected = true; + break; + + case XKB_KEY_m: + exec = HIKARI_EXEC_m; + *selected = true; + break; + + case XKB_KEY_n: + exec = HIKARI_EXEC_n; + *selected = true; + break; + + case XKB_KEY_o: + exec = HIKARI_EXEC_o; + *selected = true; + break; + + case XKB_KEY_p: + exec = HIKARI_EXEC_p; + *selected = true; + break; + + case XKB_KEY_q: + exec = HIKARI_EXEC_q; + *selected = true; + break; + + case XKB_KEY_r: + exec = HIKARI_EXEC_r; + *selected = true; + break; + + case XKB_KEY_s: + exec = HIKARI_EXEC_s; + *selected = true; + break; + + case XKB_KEY_t: + exec = HIKARI_EXEC_t; + *selected = true; + break; + + case XKB_KEY_u: + exec = HIKARI_EXEC_u; + *selected = true; + break; + + case XKB_KEY_v: + exec = HIKARI_EXEC_v; + *selected = true; + break; + + case XKB_KEY_w: + exec = HIKARI_EXEC_w; + *selected = true; + break; + + case XKB_KEY_x: + exec = HIKARI_EXEC_x; + *selected = true; + break; + + case XKB_KEY_y: + exec = HIKARI_EXEC_y; + *selected = true; + break; + + case XKB_KEY_z: + exec = HIKARI_EXEC_z; + *selected = true; + break; + + case XKB_KEY_BackSpace: + exec = NULL; + *selected = true; + break; + } + } + + return exec; +} + +static void +exec_select(struct hikari_workspace *workspace, + struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard) +{ + bool selected; + struct hikari_exec *exec = lookup_exec(event, keyboard, &selected); + + hikari_server_enter_normal_mode(NULL); + + if (exec != NULL && exec->command != NULL) { + hikari_command_execute(exec->command); + } +} + +static void +key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + struct wlr_event_keyboard_key *event = data; + + struct hikari_workspace *workspace = hikari_server.workspace; + + if (event->state == WLR_KEY_PRESSED) { + exec_select(workspace, event, keyboard); + } +} + +static void +modifier_handler(struct wl_listener *listener, void *data) +{} + +static void +button_handler(struct wl_listener *listener, void *data) +{} + +static void +render(struct hikari_output *output, struct hikari_render_data *render_data) +{} + +static void +cancel(void) +{} + +static void +cursor_move(void) +{} + +void +hikari_exec_select_mode_init(struct hikari_exec_select_mode *exec_select_mode) +{ + exec_select_mode->mode.type = HIKARI_MODE_TYPE_SELECT_EXEC; + exec_select_mode->mode.key_handler = key_handler; + exec_select_mode->mode.button_handler = button_handler; + exec_select_mode->mode.modifier_handler = modifier_handler; + exec_select_mode->mode.render = render; + exec_select_mode->mode.cancel = cancel; + exec_select_mode->mode.cursor_move = cursor_move; +} diff --git a/src/font.c b/src/font.c new file mode 100644 index 0000000..84d1ef0 --- /dev/null +++ b/src/font.c @@ -0,0 +1,37 @@ +#include + +#include +#include + +void +hikari_font_init(struct hikari_font *font, const char *font_name) +{ + font->desc = pango_font_description_from_string(font_name); +} + +void +hikari_font_fini(struct hikari_font *font) +{ + pango_font_description_free(font->desc); +} + +void +hikari_font_metrics( + struct hikari_font *font, const char *text, int *width, int *height) +{ + cairo_surface_t *surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); + + cairo_t *cairo = cairo_create(surface); + PangoLayout *layout = pango_cairo_create_layout(cairo); + + pango_layout_set_font_description(layout, font->desc); + pango_layout_set_text(layout, text, -1); + pango_cairo_update_layout(cairo, layout); + pango_cairo_show_layout(cairo, layout); + pango_layout_get_pixel_size(layout, width, height); + + g_object_unref(layout); + cairo_surface_destroy(surface); + cairo_destroy(cairo); +} diff --git a/src/geometry.c b/src/geometry.c new file mode 100644 index 0000000..4f189e3 --- /dev/null +++ b/src/geometry.c @@ -0,0 +1,55 @@ +#include + +#include + +#define SPLIT(n, x, y, width, height) \ + void hikari_geometry_split_##n(struct wlr_box *src, \ + float factor, \ + int gap, \ + struct wlr_box *dst1, \ + struct wlr_box *dst2) \ + { \ + int width = (src->width - gap) * factor; \ + int rest = src->width - gap - width * 2; \ + \ + dst1->x = src->x; \ + dst1->y = src->y; \ + dst1->width = width + rest; \ + dst1->height = src->height; \ + \ + dst2->x = src->x + dst1->width + gap; \ + dst2->y = src->y; \ + dst2->width = width; \ + dst2->height = src->height; \ + } + +SPLIT(vertical, x, y, width, height) +SPLIT(horizontal, y, x, height, width) +#undef SPLIT + +void +hikari_geometry_shrink(struct wlr_box *geometry, int gap) +{ + geometry->x += gap; + geometry->y += gap; + geometry->width -= gap * 2; + geometry->height -= gap * 2; +} + +void +hikari_geometry_constrain_position( + struct wlr_box *geometry, int screen_width, int screen_height, int x, int y) +{ + if (x + geometry->width + hikari_configuration.border > screen_width) { + geometry->x = screen_width - geometry->width - hikari_configuration.border; + } else { + geometry->x = x; + } + + if (y + geometry->height + hikari_configuration.border > screen_height) { + geometry->y = + screen_height - geometry->height - hikari_configuration.border; + } else { + geometry->y = y; + } +} diff --git a/src/group.c b/src/group.c new file mode 100644 index 0000000..87a261a --- /dev/null +++ b/src/group.c @@ -0,0 +1,61 @@ +#include + +#include +#include +#include + +#include +#include + +void +hikari_group_init(struct hikari_group *group, const char *name) +{ + int length = strlen(name) + 1; + + group->name = hikari_malloc(length); + strcpy(group->name, name); + + wl_list_init(&group->views); + wl_list_init(&group->visible_views); + + wl_list_insert(&hikari_server.groups, &group->server_groups); +} + +void +hikari_group_fini(struct hikari_group *group) +{ + hikari_free(group->name); + wl_list_remove(&group->server_groups); +} + +struct hikari_view * +hikari_group_first_view( + struct hikari_group *group, struct hikari_workspace *workspace) +{ + assert(group != NULL); + + struct hikari_view *view = NULL; + wl_list_for_each (view, &group->visible_views, visible_group_views) { + if (view->sheet->workspace == workspace) { + return view; + } + } + + return NULL; +} + +struct hikari_view * +hikari_group_last_view( + struct hikari_group *group, struct hikari_workspace *workspace) +{ + assert(group != NULL); + + struct hikari_view *view = NULL; + wl_list_for_each_reverse (view, &group->visible_views, visible_group_views) { + if (view->sheet->workspace == workspace) { + return view; + } + } + + return NULL; +} diff --git a/src/group_assign_mode.c b/src/group_assign_mode.c new file mode 100644 index 0000000..3f50811 --- /dev/null +++ b/src/group_assign_mode.c @@ -0,0 +1,412 @@ +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool +check_confirmation( + struct wlr_event_keyboard_key *event, struct hikari_keyboard *keyboard) +{ + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_Return: + return true; + break; + } + } + + return false; +} + +static bool +check_cancellation( + struct wlr_event_keyboard_key *event, struct hikari_keyboard *keyboard) +{ + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_Escape: + return true; + break; + } + } + + return false; +} + +static void +init_completion(void) +{ + struct hikari_group_assign_mode *mode = + (struct hikari_group_assign_mode *)hikari_server.mode; + + assert(mode == (struct hikari_group_assign_mode *)hikari_server.mode); + + if (mode->completion != NULL) { + return; + } + + struct hikari_group *group; + struct hikari_completion *completion = + hikari_malloc(sizeof(struct hikari_completion)); + + char *input = mode->input_buffer.buffer; + + hikari_completion_init(completion, input); + + wl_list_for_each (group, &hikari_server.groups, server_groups) { + char *name = group->name; + + if (strstr(name, input) == name && strcmp(name, input) != 0) { + hikari_completion_add(completion, name); + } + } + + mode->completion = completion; +} + +static void +fini_completion(void) +{ + struct hikari_group_assign_mode *mode = + (struct hikari_group_assign_mode *)hikari_server.mode; + + assert(mode == (struct hikari_group_assign_mode *)hikari_server.mode); + + if (mode->completion == NULL) { + return; + } + + hikari_completion_fini(mode->completion); + hikari_free(mode->completion); + mode->completion = NULL; +} + +static void +put_char(struct hikari_input_buffer *input_buffer, + struct xkb_state *xkb_state, + uint32_t keycode) +{ + uint32_t codepoint = xkb_state_key_get_utf32(xkb_state, keycode); + + if (codepoint) { + fini_completion(); + hikari_input_buffer_add_utf32_char(input_buffer, codepoint); + } +} + +static void +process_input_key(struct hikari_workspace *workspace, + struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard) +{ + struct hikari_group_assign_mode *mode = + (struct hikari_group_assign_mode *)hikari_server.mode; + + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); + + const xkb_keysym_t *syms; + uint32_t keycode = event->keycode + 8; + char *text; + + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_Caps_Lock: + case XKB_KEY_Shift_L: + case XKB_KEY_Shift_R: + case XKB_KEY_Control_L: + case XKB_KEY_Control_R: + case XKB_KEY_Meta_L: + case XKB_KEY_Meta_R: + case XKB_KEY_Alt_L: + case XKB_KEY_Alt_R: + case XKB_KEY_Super_L: + case XKB_KEY_Super_R: + break; + + case XKB_KEY_e: + if (modifiers == WLR_MODIFIER_CTRL) { + if (mode->completion != NULL) { + text = hikari_completion_cancel(mode->completion); + hikari_input_buffer_replace(&mode->input_buffer, text); + fini_completion(); + } + } else { + put_char(&mode->input_buffer, + keyboard->device->keyboard->xkb_state, + keycode); + } + break; + + case XKB_KEY_h: + fini_completion(); + if (modifiers == WLR_MODIFIER_CTRL) { + hikari_input_buffer_remove_char(&mode->input_buffer); + } else { + put_char(&mode->input_buffer, + keyboard->device->keyboard->xkb_state, + keycode); + } + break; + + case XKB_KEY_w: + fini_completion(); + if (modifiers == WLR_MODIFIER_CTRL) { + hikari_input_buffer_clear(&mode->input_buffer); + } else { + put_char(&mode->input_buffer, + keyboard->device->keyboard->xkb_state, + keycode); + } + break; + + case XKB_KEY_BackSpace: + fini_completion(); + hikari_input_buffer_remove_char(&mode->input_buffer); + break; + + case XKB_KEY_Tab: + init_completion(); + text = hikari_completion_next(mode->completion); + hikari_input_buffer_replace(&mode->input_buffer, text); + break; + + case XKB_KEY_ISO_Left_Tab: + init_completion(); + text = hikari_completion_prev(mode->completion); + hikari_input_buffer_replace(&mode->input_buffer, text); + break; + + default: + put_char(&mode->input_buffer, + keyboard->device->keyboard->xkb_state, + keycode); + break; + } + } +} + +static void +confirm_group_assign(struct hikari_workspace *workspace) +{ + struct hikari_group_assign_mode *mode = + (struct hikari_group_assign_mode *)&hikari_server.group_assign_mode; + + assert(mode == (struct hikari_group_assign_mode *)hikari_server.mode); + + struct wlr_box *geometry = hikari_view_geometry(workspace->focus_view); + + struct hikari_group *group; + + if (!strcmp(mode->input_buffer.buffer, "")) { + group = workspace->sheet->group; + hikari_indicator_update_group(&hikari_server.indicator, + geometry, + workspace->output, + "", + hikari_configuration.indicator_selected); + } else { + group = hikari_server_find_or_create_group(mode->input_buffer.buffer); + if (group->sheet == NULL) { + hikari_indicator_update_group(&hikari_server.indicator, + geometry, + workspace->output, + group->name, + hikari_configuration.indicator_selected); + } else { + hikari_indicator_update_group(&hikari_server.indicator, + geometry, + workspace->output, + "", + hikari_configuration.indicator_selected); + } + } + hikari_view_group(hikari_server.workspace->focus_view, group); + hikari_server_enter_normal_mode(NULL); +} + +static void +cancel_group_assign(struct hikari_workspace *workspace) +{ + struct wlr_box *geometry = hikari_view_geometry(workspace->focus_view); + + hikari_indicator_update_group(&hikari_server.indicator, + geometry, + workspace->output, + workspace->focus_view->group->name, + hikari_configuration.indicator_selected); + hikari_server_enter_normal_mode(NULL); +} + +static void +update_state(struct hikari_workspace *workspace, + struct hikari_keyboard *keyboard, + struct wlr_event_keyboard_key *event) +{ + struct hikari_group_assign_mode *mode = + (struct hikari_group_assign_mode *)&hikari_server.group_assign_mode; + + assert(mode == (struct hikari_group_assign_mode *)hikari_server.mode); + + struct wlr_box *geometry = hikari_view_border_geometry(workspace->focus_view); + + struct hikari_group *group; + + process_input_key(workspace, event, keyboard); + group = hikari_server_find_group(mode->input_buffer.buffer); + + if (!strcmp(mode->input_buffer.buffer, "")) { + hikari_indicator_update_group(&hikari_server.indicator, + geometry, + workspace->output, + " ", + hikari_configuration.indicator_insert); + } else { + hikari_indicator_update_group(&hikari_server.indicator, + geometry, + workspace->output, + mode->input_buffer.buffer, + hikari_configuration.indicator_insert); + } + + if (group != mode->group) { + mode->group = group; + hikari_server_refresh_indication(); + } +} + +static void +assign_group(struct hikari_workspace *workspace, + struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard) +{ + if (check_confirmation(event, keyboard)) { + confirm_group_assign(workspace); + } else if (check_cancellation(event, keyboard)) { + cancel_group_assign(workspace); + } else { + update_state(workspace, keyboard, event); + } +} + +static void +key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + struct wlr_event_keyboard_key *event = data; + + struct hikari_workspace *workspace = hikari_server.workspace; + + if (event->state == WLR_KEY_PRESSED) { + assign_group(workspace, event, keyboard); + } +} + +static void +modifier_handler(struct wl_listener *listener, void *data) +{} + +static void +render(struct hikari_output *output, struct hikari_render_data *render_data) +{ + struct hikari_group_assign_mode *mode = &hikari_server.group_assign_mode; + + assert(mode == (struct hikari_group_assign_mode *)hikari_server.mode); + + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + assert(focus_view != NULL); + + struct hikari_group *group = mode->group; + + if (group != NULL) { + struct hikari_view *view; + wl_list_for_each_reverse ( + view, &group->visible_views, visible_group_views) { + if (view->output == output && view != focus_view) { + render_data->geometry = hikari_view_border_geometry(view); + + if (hikari_group_first_view(group, hikari_server.workspace) == view) { + + hikari_indicator_frame_render(&view->indicator_frame, + hikari_configuration.indicator_first, + render_data); + } else { + hikari_indicator_frame_render(&view->indicator_frame, + hikari_configuration.indicator_grouped, + render_data); + } + } + } + } + + if (focus_view->output == output) { + render_data->geometry = hikari_view_border_geometry(focus_view); + + hikari_indicator_frame_render(&focus_view->indicator_frame, + hikari_configuration.indicator_selected, + render_data); + + hikari_indicator_render(&hikari_server.indicator, render_data); + } +} + +static void +cancel(void) +{ + hikari_input_buffer_clear(&hikari_server.group_assign_mode.input_buffer); + fini_completion(); + hikari_server.group_assign_mode.group = NULL; +} + +static void +button_handler(struct wl_listener *listener, void *data) +{} + +static void +cursor_move(void) +{} + +void +hikari_group_assign_mode_init( + struct hikari_group_assign_mode *group_assign_mode) +{ + group_assign_mode->mode.type = HIKARI_MODE_TYPE_GROUP_CHANGE; + group_assign_mode->mode.key_handler = key_handler; + group_assign_mode->mode.button_handler = button_handler; + group_assign_mode->mode.modifier_handler = modifier_handler; + group_assign_mode->mode.render = render; + group_assign_mode->mode.cancel = cancel; + group_assign_mode->mode.cursor_move = cursor_move; + group_assign_mode->group = NULL; + group_assign_mode->completion = NULL; +} diff --git a/src/indicator.c b/src/indicator.c new file mode 100644 index 0000000..ebf55fd --- /dev/null +++ b/src/indicator.c @@ -0,0 +1,156 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include + +void +hikari_indicator_init(struct hikari_indicator *indicator, float color[static 4]) +{ + int width; + hikari_font_metrics( + &hikari_configuration.font, "", &width, &indicator->bar_height); + indicator->bar_height += 8; + + int offset = 5; + hikari_indicator_bar_init(&indicator->title, indicator, offset); + offset += indicator->bar_height + 5; + hikari_indicator_bar_init(&indicator->sheet, indicator, offset); + offset += indicator->bar_height + 5; + hikari_indicator_bar_init(&indicator->group, indicator, offset); + offset += indicator->bar_height + 5; + hikari_indicator_bar_init(&indicator->mark, indicator, offset); +} + +void +hikari_indicator_fini(struct hikari_indicator *indicator) +{ + hikari_indicator_bar_fini(&indicator->title); + hikari_indicator_bar_fini(&indicator->sheet); + hikari_indicator_bar_fini(&indicator->group); + hikari_indicator_bar_fini(&indicator->mark); +} + +void +hikari_indicator_update(struct hikari_indicator *indicator, + struct hikari_view *view, + float background[static 4]) +{ + assert(view != NULL); + + struct wlr_box *geometry = hikari_view_geometry(view); + struct hikari_output *output = view->output; + + hikari_indicator_update_title( + indicator, geometry, output, view->title, background); + + hikari_indicator_update_sheet(indicator, + geometry, + output, + view->sheet, + background, + hikari_view_is_iconified(view), + hikari_view_is_floating(view)); + + if (view->sheet->group != view->group) { + hikari_indicator_update_group( + indicator, geometry, output, view->group->name, background); + } else { + hikari_indicator_update_group(indicator, geometry, output, "", background); + } + + if (view->mark != NULL) { + hikari_indicator_update_mark( + indicator, geometry, output, view->mark->name, background); + } else { + hikari_indicator_update_mark(indicator, geometry, output, "", background); + } +} + +void +hikari_indicator_render( + struct hikari_indicator *indicator, struct hikari_render_data *render_data) +{ + struct wlr_box *border_geometry = render_data->geometry; + struct wlr_box geometry = *border_geometry; + + render_data->geometry = &geometry; + + geometry.x += 5; + + struct hikari_indicator_bar *title_bar = &indicator->title; + geometry.y += 5; + hikari_indicator_bar_render(title_bar, render_data); + + struct hikari_indicator_bar *sheet_bar = &indicator->sheet; + geometry.y += indicator->bar_height + 5; + hikari_indicator_bar_render(sheet_bar, render_data); + + struct hikari_indicator_bar *group_bar = &indicator->group; + geometry.y += indicator->bar_height + 5; + hikari_indicator_bar_render(group_bar, render_data); + + struct hikari_indicator_bar *mark_bar = &indicator->mark; + geometry.y += indicator->bar_height + 5; + hikari_indicator_bar_render(mark_bar, render_data); + + render_data->geometry = border_geometry; +} + +void +hikari_indicator_update_sheet(struct hikari_indicator *indicator, + struct wlr_box *view_geometry, + struct hikari_output *output, + struct hikari_sheet *sheet, + float background[static 4], + bool iconified, + bool floating) +{ + char text[9]; + int i = 0; + + if (floating) { + text[i++] = '~'; + } + + if (iconified) { + text[i++] = '['; + text[i++] = sheet->group->name[0]; + text[i++] = ']'; + } else { + text[i++] = sheet->group->name[0]; + } + + if (sheet->workspace->sheet != sheet) { + text[i++] = ' '; + text[i++] = '@'; + text[i++] = ' '; + text[i++] = sheet->workspace->sheet->group->name[0]; + } + + text[i] = '\0'; + + hikari_indicator_bar_update( + &indicator->sheet, view_geometry, output, text, background); +} + +void +hikari_indicator_damage( + struct hikari_indicator *indicator, struct hikari_view *view) +{ + assert(indicator != NULL); + assert(view != NULL); + + struct wlr_box *geometry = hikari_view_border_geometry(view); + struct hikari_output *output = view->output; + + hikari_indicator_bar_damage(&indicator->title, geometry, output); + hikari_indicator_bar_damage(&indicator->sheet, geometry, output); + hikari_indicator_bar_damage(&indicator->group, geometry, output); + hikari_indicator_bar_damage(&indicator->mark, geometry, output); +} diff --git a/src/indicator_bar.c b/src/indicator_bar.c new file mode 100644 index 0000000..fad27eb --- /dev/null +++ b/src/indicator_bar.c @@ -0,0 +1,154 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +void +hikari_indicator_bar_init(struct hikari_indicator_bar *indicator_bar, + struct hikari_indicator *indicator, + int offset) +{ + indicator_bar->texture = NULL; + indicator_bar->indicator = indicator; + indicator_bar->offset = offset; +} + +void +hikari_indicator_bar_fini(struct hikari_indicator_bar *indicator_bar) +{ + wlr_texture_destroy(indicator_bar->texture); + indicator_bar->texture = NULL; +} + +static void +damage(struct hikari_indicator_bar *indicator_bar, + struct wlr_box *geometry, + struct hikari_output *output) +{ + geometry->x += 5; + geometry->y += indicator_bar->offset; + geometry->width = indicator_bar->width; + geometry->height = indicator_bar->indicator->bar_height; + + hikari_output_add_damage(output, geometry); +} + +void +hikari_indicator_bar_damage(struct hikari_indicator_bar *indicator_bar, + struct wlr_box *view_geometry, + struct hikari_output *output) +{ + struct wlr_box geometry = *view_geometry; + + damage(indicator_bar, &geometry, output); +} + +void +hikari_indicator_bar_update(struct hikari_indicator_bar *indicator_bar, + struct wlr_box *view_geometry, + struct hikari_output *output, + const char *text, + float background[static 4]) +{ + if (indicator_bar->texture != NULL) { + hikari_indicator_bar_fini(indicator_bar); + } + + if (text == NULL || !strcmp(text, "")) { + return; + } + + int font_width, font_height; + int width, height; + struct hikari_font *font = &hikari_configuration.font; + struct wlr_box geometry = *view_geometry; + + damage(indicator_bar, &geometry, output); + + hikari_font_metrics(font, text, &font_width, &font_height); + + width = geometry.width = indicator_bar->width = font_width + 8; + height = indicator_bar->indicator->bar_height; + + hikari_output_add_damage(output, &geometry); + + cairo_surface_t *surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + + cairo_t *cairo = cairo_create(surface); + PangoLayout *layout = pango_cairo_create_layout(cairo); + + cairo_set_source_rgba( + cairo, background[0], background[1], background[2], background[3]); + cairo_paint(cairo); + + cairo_set_source_rgba(cairo, + hikari_configuration.border_inactive[0], + hikari_configuration.border_inactive[1], + hikari_configuration.border_inactive[2], + hikari_configuration.border_inactive[3]); + cairo_rectangle(cairo, 0, 0, width, height); + cairo_set_line_width(cairo, 1); + cairo_stroke(cairo); + + cairo_set_source_rgba(cairo, 0, 0, 0, 1); + pango_layout_set_font_description(layout, font->desc); + cairo_move_to(cairo, 4, 4); + pango_layout_set_text(layout, text, -1); + + pango_cairo_update_layout(cairo, layout); + pango_cairo_show_layout(cairo, layout); + + cairo_surface_flush(surface); + + unsigned char *data = cairo_image_surface_get_data(surface); + int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); + + struct wlr_renderer *renderer = wlr_backend_get_renderer( + hikari_server.workspace->output->output->backend); + + indicator_bar->texture = wlr_texture_from_pixels( + renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); + + cairo_surface_destroy(surface); + g_object_unref(layout); + cairo_destroy(cairo); +} + +void +hikari_indicator_bar_render(struct hikari_indicator_bar *indicator_bar, + struct hikari_render_data *render_data) +{ + if (indicator_bar->texture == NULL) { + return; + } + + struct wlr_box *geometry = render_data->geometry; + struct wlr_renderer *renderer = render_data->renderer; + struct wlr_output *output = render_data->output; + + float matrix[9]; + + geometry->width = indicator_bar->width; + geometry->height = indicator_bar->indicator->bar_height; + + wlr_renderer_scissor(renderer, geometry); + wlr_matrix_project_box(matrix, geometry, 0, 0, output->transform_matrix); + + wlr_render_texture_with_matrix(renderer, indicator_bar->texture, matrix, 1); +} diff --git a/src/indicator_frame.c b/src/indicator_frame.c new file mode 100644 index 0000000..9791cbf --- /dev/null +++ b/src/indicator_frame.c @@ -0,0 +1,113 @@ +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +void +hikari_indicator_frame_render(struct hikari_indicator_frame *indicator_frame, + float color[static 4], + struct hikari_render_data *render_data) +{ + struct wlr_box *box = render_data->geometry; + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_union_rect( + &damage, &damage, box->x, box->y, box->width, box->height); + + pixman_region32_intersect(&damage, &damage, render_data->damage); + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto buffer_damage_finish; + } + + struct wlr_renderer *renderer = render_data->renderer; + assert(renderer); + + float top_matrix[9]; + float bottom_matrix[9]; + float left_matrix[9]; + float right_matrix[9]; + + wlr_matrix_project_box(top_matrix, + &indicator_frame->top, + WL_OUTPUT_TRANSFORM_NORMAL, + 0, + render_data->output->transform_matrix); + + wlr_matrix_project_box(bottom_matrix, + &indicator_frame->bottom, + WL_OUTPUT_TRANSFORM_NORMAL, + 0, + render_data->output->transform_matrix); + + wlr_matrix_project_box(left_matrix, + &indicator_frame->left, + WL_OUTPUT_TRANSFORM_NORMAL, + 0, + render_data->output->transform_matrix); + + wlr_matrix_project_box(right_matrix, + &indicator_frame->right, + WL_OUTPUT_TRANSFORM_NORMAL, + 0, + render_data->output->transform_matrix); + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; i++) { + hikari_output_scissor_render(render_data->output, renderer, &rects[i]); + wlr_render_quad_with_matrix(renderer, color, top_matrix); + wlr_render_quad_with_matrix(renderer, color, bottom_matrix); + wlr_render_quad_with_matrix(renderer, color, left_matrix); + wlr_render_quad_with_matrix(renderer, color, right_matrix); + } + +buffer_damage_finish: + pixman_region32_fini(&damage); +} + +void +hikari_indicator_frame_refresh_geometry( + struct hikari_indicator_frame *indicator_frame, struct hikari_view *view) +{ + struct wlr_box *geometry; + + if (view->border.state == HIKARI_BORDER_NONE) { + geometry = hikari_view_geometry(view); + } else { + geometry = hikari_view_border_geometry(view); + } + + indicator_frame->top.x = geometry->x; + indicator_frame->top.y = geometry->y; + indicator_frame->top.width = geometry->width; + indicator_frame->top.height = hikari_configuration.border; + + indicator_frame->bottom.x = geometry->x; + indicator_frame->bottom.y = + geometry->y + geometry->height - hikari_configuration.border; + indicator_frame->bottom.width = geometry->width; + indicator_frame->bottom.height = hikari_configuration.border; + + indicator_frame->left.x = geometry->x; + indicator_frame->left.y = geometry->y; + indicator_frame->left.width = hikari_configuration.border; + indicator_frame->left.height = geometry->height; + + indicator_frame->right.x = + geometry->x + geometry->width - hikari_configuration.border; + indicator_frame->right.y = geometry->y; + indicator_frame->right.width = hikari_configuration.border; + indicator_frame->right.height = geometry->height; +} diff --git a/src/input_buffer.c b/src/input_buffer.c new file mode 100644 index 0000000..967ae4b --- /dev/null +++ b/src/input_buffer.c @@ -0,0 +1,81 @@ +#include + +#include + +#include + +struct hikari_input_buffer * +hikari_input_buffer_init( + struct hikari_input_buffer *input_buffer, char *content) +{ + strncpy(input_buffer->buffer, content, sizeof(input_buffer->buffer) - 1); + + input_buffer->pos = + strnlen(input_buffer->buffer, sizeof(input_buffer->buffer)); + + return input_buffer; +} + +void +hikari_input_buffer_add_char( + struct hikari_input_buffer *input_buffer, char input) +{ + if (input_buffer->pos == sizeof(input_buffer->buffer) - 1) { + return; + } + + input_buffer->buffer[input_buffer->pos] = input; + input_buffer->pos++; +} + +void +hikari_input_buffer_add_utf32_char( + struct hikari_input_buffer *input_buffer, uint32_t codepoint) +{ + size_t length = utf8_chsize(codepoint); + + if (input_buffer->pos + length > sizeof(input_buffer->buffer) - 1) { + return; + } + + utf8_encode(&input_buffer->buffer[input_buffer->pos], length, codepoint); + input_buffer->pos += length; +} + +void +hikari_input_buffer_remove_char(struct hikari_input_buffer *input_buffer) +{ + if (input_buffer->pos == 0) { + input_buffer->buffer[0] = '\0'; + return; + } + + input_buffer->pos--; + input_buffer->buffer[input_buffer->pos] = '\0'; +} + +void +hikari_input_buffer_clear(struct hikari_input_buffer *input_buffer) +{ + memset(input_buffer->buffer, 0, sizeof(input_buffer->buffer)); + input_buffer->pos = 0; +} + +void +hikari_input_buffer_replace( + struct hikari_input_buffer *input_buffer, char *content) +{ + hikari_input_buffer_clear(input_buffer); + char *c = content; + + for (int i = 0; i < sizeof(input_buffer->buffer) && *c != '\0'; i++) { + hikari_input_buffer_add_char(input_buffer, *c); + c++; + } +} + +char * +hikari_input_buffer_read(struct hikari_input_buffer *input_buffer) +{ + return input_buffer->buffer; +} diff --git a/src/keybinding.c b/src/keybinding.c new file mode 100644 index 0000000..a16e403 --- /dev/null +++ b/src/keybinding.c @@ -0,0 +1,14 @@ +#include + +#include + +#include + +void +hikari_keybinding_fini(struct hikari_keybinding *keybinding) +{ + assert(keybinding != NULL); + if (keybinding->cleanup != NULL) { + keybinding->cleanup(keybinding->arg); + } +} diff --git a/src/keyboard.c b/src/keyboard.c new file mode 100644 index 0000000..a341d73 --- /dev/null +++ b/src/keyboard.c @@ -0,0 +1,118 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +update_mod_state(struct hikari_keyboard *keyboard) +{ + uint32_t modifier_keys = + wlr_keyboard_get_modifiers(keyboard->device->keyboard); + + bool was_pressed = hikari_server.keyboard_state.mod_pressed; + bool is_pressed = modifier_keys & WLR_MODIFIER_LOGO; + + hikari_server.keyboard_state.modifiers = modifier_keys; + hikari_server.keyboard_state.mod_released = was_pressed && !is_pressed; + hikari_server.keyboard_state.mod_changed = was_pressed != is_pressed; + hikari_server.keyboard_state.mod_pressed = is_pressed; +} + +static void +key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + + if (hikari_server.locked) { + hikari_unlocker_key_handler(listener, data); + return; + } + + hikari_server.mode->key_handler(listener, data); +} + +static void +modifiers_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = + wl_container_of(listener, keyboard, modifiers); + + update_mod_state(keyboard); + + if (hikari_server.locked) { + return; + } + + hikari_server.mode->modifier_handler(listener, data); +} + +static void +destroy_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = + wl_container_of(listener, keyboard, destroy); + + hikari_keyboard_fini(keyboard); + hikari_free(keyboard); + + /* uint32_t caps = WL_SEAT_CAPABILITY_POINTER; */ + /* wlr_seat_set_capabilities(hikari_server.seat, caps); */ +} + +void +hikari_keyboard_init( + struct hikari_keyboard *keyboard, struct wlr_input_device *device) +{ + keyboard->device = device; + + struct xkb_rule_names rules = { 0 }; + rules.rules = getenv("XKB_DEFAULT_RULES"); + rules.model = getenv("XKB_DEFAULT_MODEL"); + rules.layout = getenv("XKB_DEFAULT_LAYOUT"); + rules.variant = getenv("XKB_DEFAULT_VARIANT"); + rules.options = getenv("XKB_DEFAULT_OPTIONS"); + + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_keymap *keymap = + xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); + + wlr_keyboard_set_keymap(device->keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + wlr_keyboard_set_repeat_info(device->keyboard, 25, 600); + + keyboard->modifiers.notify = modifiers_handler; + wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers); + + keyboard->key.notify = key_handler; + wl_signal_add(&device->keyboard->events.key, &keyboard->key); + + keyboard->destroy.notify = destroy_handler; + wl_signal_add(&device->keyboard->events.destroy, &keyboard->destroy); + + wlr_seat_set_keyboard(hikari_server.seat, device); + + wl_list_insert(&hikari_server.keyboards, &keyboard->link); +} + +void +hikari_keyboard_fini(struct hikari_keyboard *keyboard) +{ + wl_list_remove(&keyboard->modifiers.link); + wl_list_remove(&keyboard->key.link); + wl_list_remove(&keyboard->destroy.link); + wl_list_remove(&keyboard->link); +} diff --git a/src/keyboard_grab_mode.c b/src/keyboard_grab_mode.c new file mode 100644 index 0000000..fb39d1c --- /dev/null +++ b/src/keyboard_grab_mode.c @@ -0,0 +1,110 @@ +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +keyboard_grab_key_handler(struct hikari_workspace *workspace, + struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard) +{ + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); + + if ((modifiers & WLR_MODIFIER_LOGO) && event->state == WLR_KEY_PRESSED) { + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_g: + if (modifiers == + (WLR_MODIFIER_LOGO | WLR_MODIFIER_ALT | WLR_MODIFIER_CTRL)) { + hikari_server_enter_normal_mode(NULL); + hikari_server_refresh_indication(); + hikari_server_cursor_focus(); + return; + } + } + } + } + + wlr_seat_set_keyboard(hikari_server.seat, keyboard->device); + wlr_seat_keyboard_notify_key( + hikari_server.seat, event->time_msec, event->keycode, event->state); +} + +static void +render(struct hikari_output *output, struct hikari_render_data *render_data) +{ + assert(hikari_server.workspace->focus_view != NULL); + + struct hikari_view *view = hikari_server.workspace->focus_view; + + if (view->output == output) { + render_data->geometry = hikari_view_border_geometry(view); + hikari_indicator_frame_render(&view->indicator_frame, + hikari_configuration.indicator_insert, + render_data); + } +} + +static void +modifier_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = + wl_container_of(listener, keyboard, modifiers); + + wlr_seat_keyboard_notify_modifiers( + hikari_server.seat, &keyboard->device->keyboard->modifiers); +} + +static void +key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + + struct wlr_event_keyboard_key *event = data; + struct hikari_workspace *workspace = hikari_server.workspace; + + keyboard_grab_key_handler(workspace, event, keyboard); +} + +static void +cancel(void) +{} + +static void +button_handler(struct wl_listener *listener, void *data) +{} + +static void +cursor_move(void) +{} + +void +hikari_keyboard_grab_mode_init( + struct hikari_keyboard_grab_mode *keyboard_grab_mode) +{ + keyboard_grab_mode->mode.type = HIKARI_MODE_TYPE_GRAB_KEYBOARD; + keyboard_grab_mode->mode.key_handler = key_handler; + keyboard_grab_mode->mode.button_handler = button_handler; + keyboard_grab_mode->mode.modifier_handler = modifier_handler; + keyboard_grab_mode->mode.render = render; + keyboard_grab_mode->mode.cancel = cancel; + keyboard_grab_mode->mode.cursor_move = cursor_move; +} diff --git a/src/layout.c b/src/layout.c new file mode 100644 index 0000000..840af4e --- /dev/null +++ b/src/layout.c @@ -0,0 +1,47 @@ +#include + +#include + +#include +#include +#include + +void +hikari_layout_init(struct hikari_layout *layout, struct hikari_split *split) +{ + layout->split = split; + wl_list_init(&layout->tiles); +} + +void +hikari_layout_fini(struct hikari_layout *layout) +{ + /* hikari_split_fini(layout->split); */ +} + +#define CYCLE_LAYOUT(name, link) \ + struct hikari_view *hikari_layout_##name##_view( \ + struct hikari_layout *layout) \ + { \ + struct wl_list *link = layout->tiles.link; \ + struct hikari_tile *link##_tile; \ + \ + do { \ + if (link == &layout->tiles) { \ + return NULL; \ + } else { \ + link##_tile = wl_container_of(link, link##_tile, layout_tiles); \ + } \ + link = link->link; \ + } while (hikari_view_is_hidden(link##_tile->view)); \ + \ + assert(link##_tile != NULL); \ + assert(link##_tile->view != NULL); \ + assert(!hikari_view_is_hidden(link##_tile->view)); \ + \ + return link##_tile->view; \ + } + +CYCLE_LAYOUT(first, next) +CYCLE_LAYOUT(last, prev) +#undef CYCLE_LAYOUT diff --git a/src/mark.c b/src/mark.c new file mode 100644 index 0000000..3f95f4c --- /dev/null +++ b/src/mark.c @@ -0,0 +1,58 @@ +#include + +#include +#include +#include + +#include +#include + +struct hikari_mark marks[HIKARI_NR_OF_MARKS]; + +void +hikari_marks_init(void) +{ + char *name; + for (int i = 0; i < HIKARI_NR_OF_MARKS; i++) { + name = hikari_malloc(2); + snprintf(name, 2, "%c", 'a' + i); + marks[i].name = name; + marks[i].view = NULL; + } +} + +void +hikari_marks_fini(void) +{ + for (int i = 0; i < HIKARI_NR_OF_MARKS; i++) { + hikari_free(marks[i].name); + } +} + +void +hikari_mark_clear(struct hikari_mark *mark) +{ + assert(mark != NULL); + + if (mark->view != NULL) { + mark->view->mark = NULL; + mark->view = NULL; + ; + } +} + +void +hikari_mark_set(struct hikari_mark *mark, struct hikari_view *view) +{ + assert(mark != NULL); + assert(view != NULL); + + if (view->mark != NULL) { + hikari_mark_clear(view->mark); + } + + hikari_mark_clear(mark); + + mark->view = view; + view->mark = mark; +} diff --git a/src/mark_assign_mode.c b/src/mark_assign_mode.c new file mode 100644 index 0000000..bc39627 --- /dev/null +++ b/src/mark_assign_mode.c @@ -0,0 +1,432 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool +check_confirmation( + struct wlr_event_keyboard_key *event, struct hikari_keyboard *keyboard) +{ + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_Return: + return true; + break; + } + } + + return false; +} + +static bool +check_cancellation( + struct wlr_event_keyboard_key *event, struct hikari_keyboard *keyboard) +{ + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_Escape: + return true; + break; + } + } + + return false; +} + +static struct hikari_mark * +lookup_mark(struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard, + bool *selected) +{ + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + *selected = false; + struct hikari_mark *mark = NULL; + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_a: + mark = HIKARI_MARK_a; + *selected = true; + break; + + case XKB_KEY_b: + mark = HIKARI_MARK_b; + *selected = true; + break; + + case XKB_KEY_c: + mark = HIKARI_MARK_c; + *selected = true; + break; + + case XKB_KEY_d: + mark = HIKARI_MARK_d; + *selected = true; + break; + + case XKB_KEY_e: + mark = HIKARI_MARK_e; + *selected = true; + break; + + case XKB_KEY_f: + mark = HIKARI_MARK_f; + *selected = true; + break; + + case XKB_KEY_g: + mark = HIKARI_MARK_g; + *selected = true; + break; + + case XKB_KEY_h: + mark = HIKARI_MARK_h; + *selected = true; + break; + + case XKB_KEY_i: + mark = HIKARI_MARK_i; + *selected = true; + break; + + case XKB_KEY_j: + mark = HIKARI_MARK_j; + *selected = true; + break; + + case XKB_KEY_k: + mark = HIKARI_MARK_k; + *selected = true; + break; + + case XKB_KEY_l: + mark = HIKARI_MARK_l; + *selected = true; + break; + + case XKB_KEY_m: + mark = HIKARI_MARK_m; + *selected = true; + break; + + case XKB_KEY_n: + mark = HIKARI_MARK_n; + *selected = true; + break; + + case XKB_KEY_o: + mark = HIKARI_MARK_o; + *selected = true; + break; + + case XKB_KEY_p: + mark = HIKARI_MARK_p; + *selected = true; + break; + + case XKB_KEY_q: + mark = HIKARI_MARK_q; + *selected = true; + break; + + case XKB_KEY_r: + mark = HIKARI_MARK_r; + *selected = true; + break; + + case XKB_KEY_s: + mark = HIKARI_MARK_s; + *selected = true; + break; + + case XKB_KEY_t: + mark = HIKARI_MARK_t; + *selected = true; + break; + + case XKB_KEY_u: + mark = HIKARI_MARK_u; + *selected = true; + break; + + case XKB_KEY_v: + mark = HIKARI_MARK_v; + *selected = true; + break; + + case XKB_KEY_w: + mark = HIKARI_MARK_w; + *selected = true; + break; + + case XKB_KEY_x: + mark = HIKARI_MARK_x; + *selected = true; + break; + + case XKB_KEY_y: + mark = HIKARI_MARK_y; + *selected = true; + break; + + case XKB_KEY_z: + mark = HIKARI_MARK_z; + *selected = true; + break; + + case XKB_KEY_BackSpace: + mark = NULL; + *selected = true; + break; + } + } + + return mark; +} + +static void +clear_conflict(void) +{ + struct hikari_mark_assign_mode *mode = &hikari_server.mark_assign_mode; + + assert(mode == (struct hikari_mark_assign_mode *)hikari_server.mode); + + struct hikari_mark *mark = mode->pending_mark; + + if (mark != NULL && mark->view != NULL) { + hikari_view_damage_whole(mark->view); + hikari_indicator_damage(&mode->indicator, mark->view); + } +} + +static void +update_state(struct hikari_mark *mark, + struct wlr_box *geometry, + struct hikari_output *output) +{ + struct hikari_mark_assign_mode *mode = &hikari_server.mark_assign_mode; + + assert(mode == (struct hikari_mark_assign_mode *)hikari_server.mode); + + if (mark) { + hikari_indicator_update_mark(&hikari_server.indicator, + geometry, + output, + mark->name, + hikari_configuration.indicator_insert); + } else { + hikari_indicator_update_mark(&hikari_server.indicator, + geometry, + output, + " ", + hikari_configuration.indicator_insert); + } + + if (mode->pending_mark != mark) { + clear_conflict(); + + if (mark != NULL && mark->view != NULL) { + hikari_indicator_update(&mode->indicator, + mark->view, + hikari_configuration.indicator_conflict); + + hikari_view_damage_whole(mark->view); + } + + mode->pending_mark = mark; + } +} + +static void +confirm_mark_assign(struct wlr_box *geometry, struct hikari_output *output) +{ + struct hikari_mark_assign_mode *mode = &hikari_server.mark_assign_mode; + + assert(mode == (struct hikari_mark_assign_mode *)hikari_server.mode); + + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + struct hikari_mark *mark = mode->pending_mark; + + if (mark) { + clear_conflict(); + + hikari_mark_set(mark, focus_view); + hikari_indicator_update_mark(&hikari_server.indicator, + geometry, + output, + mode->pending_mark->name, + hikari_configuration.indicator_selected); + + mode->pending_mark = NULL; + } else { + if (focus_view->mark != NULL) { + hikari_mark_clear(focus_view->mark); + } + + hikari_indicator_update_mark(&hikari_server.indicator, + geometry, + output, + "", + hikari_configuration.indicator_selected); + } + + hikari_server_enter_normal_mode(NULL); +} + +static void +cancel_mark_assign(struct hikari_mark *mark, + struct wlr_box *geometry, + struct hikari_output *output) +{ + assert(&hikari_server.mark_assign_mode.mode == hikari_server.mode); + + struct hikari_view *view = hikari_server.workspace->focus_view; + + clear_conflict(); + + if (view->mark != NULL) { + hikari_indicator_update_mark(&hikari_server.indicator, + geometry, + output, + view->mark->name, + hikari_configuration.indicator_selected); + } else { + hikari_indicator_update_mark(&hikari_server.indicator, + geometry, + output, + "", + hikari_configuration.indicator_selected); + } + + hikari_server_enter_normal_mode(NULL); +} + +static void +assign_mark(struct hikari_workspace *workspace, + struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard) +{ + assert(hikari_server.workspace->focus_view != NULL); + + bool selected; + + struct hikari_mark *mark = lookup_mark(event, keyboard, &selected); + struct wlr_box *geometry = hikari_view_border_geometry(workspace->focus_view); + struct hikari_output *output = workspace->output; + + if (selected) { + update_state(mark, geometry, output); + hikari_server_refresh_indication(); + } else if (check_confirmation(event, keyboard)) { + confirm_mark_assign(geometry, output); + } else if (check_cancellation(event, keyboard)) { + cancel_mark_assign(mark, geometry, output); + hikari_server_refresh_indication(); + } +} + +static void +key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + struct wlr_event_keyboard_key *event = data; + + struct hikari_workspace *workspace = hikari_server.workspace; + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); + + if (event->state == WLR_KEY_PRESSED && modifiers == 0) { + assign_mark(workspace, event, keyboard); + } +} + +static void +modifier_handler(struct wl_listener *listener, void *data) +{} + +static void +render(struct hikari_output *output, struct hikari_render_data *render_data) +{ + assert(hikari_server.workspace->focus_view != NULL); + struct hikari_view *view = hikari_server.workspace->focus_view; + + struct hikari_mark_assign_mode *mode = + (struct hikari_mark_assign_mode *)hikari_server.mode; + + if (mode->pending_mark != NULL && mode->pending_mark->view != NULL && + mode->pending_mark->view->output == output) { + render_data->geometry = + hikari_view_border_geometry(mode->pending_mark->view); + + hikari_indicator_frame_render(&mode->pending_mark->view->indicator_frame, + hikari_configuration.indicator_conflict, + render_data); + hikari_indicator_render(&mode->indicator, render_data); + } + + if (view->output == output) { + render_data->geometry = hikari_view_border_geometry(view); + + hikari_indicator_frame_render(&view->indicator_frame, + hikari_configuration.indicator_selected, + render_data); + + hikari_indicator_render(&hikari_server.indicator, render_data); + } +} + +static void +cancel(void) +{ + hikari_server.mark_assign_mode.pending_mark = NULL; +} + +static void +button_handler(struct wl_listener *listener, void *data) +{} + +static void +cursor_move(void) +{} + +void +hikari_mark_assign_mode_init(struct hikari_mark_assign_mode *mark_assign_mode) +{ + mark_assign_mode->mode.type = HIKARI_MODE_TYPE_ASSIGN_MARK; + mark_assign_mode->mode.key_handler = key_handler; + mark_assign_mode->mode.button_handler = button_handler; + mark_assign_mode->mode.modifier_handler = modifier_handler; + mark_assign_mode->mode.render = render; + mark_assign_mode->mode.cancel = cancel; + mark_assign_mode->mode.cursor_move = cursor_move; + + hikari_indicator_init( + &mark_assign_mode->indicator, hikari_configuration.indicator_conflict); +} + +void +hikari_mark_assign_mode_fini(struct hikari_mark_assign_mode *mark_assign_mode) +{ + hikari_indicator_fini(&mark_assign_mode->indicator); +} diff --git a/src/mark_select_mode.c b/src/mark_select_mode.c new file mode 100644 index 0000000..7deae17 --- /dev/null +++ b/src/mark_select_mode.c @@ -0,0 +1,250 @@ +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct hikari_mark * +lookup_mark(struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard, + bool *selected) +{ + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + *selected = false; + struct hikari_mark *mark = NULL; + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_a: + mark = HIKARI_MARK_a; + *selected = true; + break; + + case XKB_KEY_b: + mark = HIKARI_MARK_b; + *selected = true; + break; + + case XKB_KEY_c: + mark = HIKARI_MARK_c; + *selected = true; + break; + + case XKB_KEY_d: + mark = HIKARI_MARK_d; + *selected = true; + break; + + case XKB_KEY_e: + mark = HIKARI_MARK_e; + *selected = true; + break; + + case XKB_KEY_f: + mark = HIKARI_MARK_f; + *selected = true; + break; + + case XKB_KEY_g: + mark = HIKARI_MARK_g; + *selected = true; + break; + + case XKB_KEY_h: + mark = HIKARI_MARK_h; + *selected = true; + break; + + case XKB_KEY_i: + mark = HIKARI_MARK_i; + *selected = true; + break; + + case XKB_KEY_j: + mark = HIKARI_MARK_j; + *selected = true; + break; + + case XKB_KEY_k: + mark = HIKARI_MARK_k; + *selected = true; + break; + + case XKB_KEY_l: + mark = HIKARI_MARK_l; + *selected = true; + break; + + case XKB_KEY_m: + mark = HIKARI_MARK_m; + *selected = true; + break; + + case XKB_KEY_n: + mark = HIKARI_MARK_n; + *selected = true; + break; + + case XKB_KEY_o: + mark = HIKARI_MARK_o; + *selected = true; + break; + + case XKB_KEY_p: + mark = HIKARI_MARK_p; + *selected = true; + break; + + case XKB_KEY_q: + mark = HIKARI_MARK_q; + *selected = true; + break; + + case XKB_KEY_r: + mark = HIKARI_MARK_r; + *selected = true; + break; + + case XKB_KEY_s: + mark = HIKARI_MARK_s; + *selected = true; + break; + + case XKB_KEY_t: + mark = HIKARI_MARK_t; + *selected = true; + break; + + case XKB_KEY_u: + mark = HIKARI_MARK_u; + *selected = true; + break; + + case XKB_KEY_v: + mark = HIKARI_MARK_v; + *selected = true; + break; + + case XKB_KEY_w: + mark = HIKARI_MARK_w; + *selected = true; + break; + + case XKB_KEY_x: + mark = HIKARI_MARK_x; + *selected = true; + break; + + case XKB_KEY_y: + mark = HIKARI_MARK_y; + *selected = true; + break; + + case XKB_KEY_z: + mark = HIKARI_MARK_z; + *selected = true; + break; + + case XKB_KEY_BackSpace: + mark = NULL; + *selected = true; + break; + } + } + + return mark; +} + +static void +mark_select(struct hikari_workspace *workspace, + struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard) +{ + bool selected; + struct hikari_mark *mark = lookup_mark(event, keyboard, &selected); + + hikari_server_enter_normal_mode(NULL); + + if (mark != NULL && mark->view != NULL) { + struct hikari_view *view = mark->view; + + if (hikari_server.mark_select_mode.switch_workspace) { + hikari_workspace_switch_sheet(view->sheet->workspace, view->sheet); + } + + if (!hikari_view_is_hidden(view)) { + hikari_view_raise(view); + } else { + hikari_view_raise_hidden(view); + hikari_view_show(view); + } + + if (view != hikari_server.workspace->focus_view) { + hikari_workspace_focus_view(workspace, view); + } + + hikari_view_center_cursor(view); + } + + hikari_server_cursor_focus(); +} + +static void +key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + struct wlr_event_keyboard_key *event = data; + + struct hikari_workspace *workspace = hikari_server.workspace; + + if (event->state == WLR_KEY_PRESSED) { + mark_select(workspace, event, keyboard); + } +} + +static void +modifier_handler(struct wl_listener *listener, void *data) +{} + +static void +render(struct hikari_output *output, struct hikari_render_data *render_data) +{} + +static void +cancel(void) +{} + +static void +button_handler(struct wl_listener *listener, void *data) +{} + +static void +cursor_move(void) +{} + +void +hikari_mark_select_mode_init(struct hikari_mark_select_mode *mark_select_mode) +{ + mark_select_mode->mode.type = HIKARI_MODE_TYPE_SELECT_MARK; + mark_select_mode->mode.key_handler = key_handler; + mark_select_mode->mode.button_handler = button_handler; + mark_select_mode->mode.modifier_handler = modifier_handler; + mark_select_mode->mode.render = render; + mark_select_mode->mode.cancel = cancel; + mark_select_mode->mode.cursor_move = cursor_move; + + mark_select_mode->switch_workspace = false; +} diff --git a/src/maximized_state.c b/src/maximized_state.c new file mode 100644 index 0000000..0dc83a5 --- /dev/null +++ b/src/maximized_state.c @@ -0,0 +1,24 @@ +#include + +#include + +struct hikari_maximized_state * +hikari_maximized_state_full(struct hikari_view *view, int width, int height) +{ + struct hikari_maximized_state *maximized_state; + + if (view->maximized_state == NULL) { + maximized_state = hikari_maximized_state_alloc(); + } else { + maximized_state = view->maximized_state; + } + + maximized_state->maximization = HIKARI_MAXIMIZATION_FULLY_MAXIMIZED; + + maximized_state->geometry.x = 0; + maximized_state->geometry.y = 0; + maximized_state->geometry.width = width; + maximized_state->geometry.height = height; + + return maximized_state; +} diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..3621260 --- /dev/null +++ b/src/memory.c @@ -0,0 +1,21 @@ +#include + +#include + +void * +hikari_malloc(size_t size) +{ + return malloc(size); +} + +void * +hikari_calloc(size_t number, size_t size) +{ + return calloc(number, size); +} + +void +hikari_free(void *ptr) +{ + return free(ptr); +} diff --git a/src/move_mode.c b/src/move_mode.c new file mode 100644 index 0000000..02b884b --- /dev/null +++ b/src/move_mode.c @@ -0,0 +1,93 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +static void +cancel(void) +{ + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + if (focus_view != NULL) { + hikari_indicator_update(&hikari_server.indicator, + focus_view, + hikari_configuration.indicator_selected); + + hikari_view_center_cursor(focus_view); + } +} + +static void +key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + + struct wlr_event_keyboard_key *event = data; + + if (event->state == WLR_KEY_RELEASED) { + hikari_server_enter_normal_mode(NULL); + } +} + +static void +modifier_handler(struct wl_listener *listener, void *data) +{} + +static void +render(struct hikari_output *output, struct hikari_render_data *render_data) +{ + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + if (focus_view->output == output) { + render_data->geometry = hikari_view_border_geometry(focus_view); + + hikari_indicator_frame_render(&focus_view->indicator_frame, + hikari_configuration.indicator_insert, + render_data); + + hikari_indicator_render(&hikari_server.indicator, render_data); + } +} + +static void +cursor_move(void) +{ + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + assert(focus_view != NULL); + + struct hikari_output *output = focus_view->output; + + hikari_view_move_absolute(focus_view, + hikari_server.cursor->x - output->geometry.x, + hikari_server.cursor->y - output->geometry.y); +} + +static void +button_handler(struct wl_listener *listener, void *data) +{ + struct wlr_event_pointer_button *event = data; + + if (event->state == WLR_BUTTON_RELEASED) { + hikari_server_enter_normal_mode(NULL); + } +} + +void +hikari_move_mode_init(struct hikari_move_mode *move_mode) +{ + move_mode->mode.type = HIKARI_MODE_TYPE_MOVE; + move_mode->mode.key_handler = key_handler; + move_mode->mode.button_handler = button_handler; + move_mode->mode.modifier_handler = modifier_handler; + move_mode->mode.render = render; + move_mode->mode.cancel = cancel; + move_mode->mode.cursor_move = cursor_move; +} diff --git a/src/normal_mode.c b/src/normal_mode.c new file mode 100644 index 0000000..8f5fc44 --- /dev/null +++ b/src/normal_mode.c @@ -0,0 +1,238 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void +normal_mode_keyboard_handler(struct hikari_workspace *workspace, + struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard) +{ + if (event->state == WLR_KEY_PRESSED) { + uint32_t modifiers = hikari_server.keyboard_state.modifiers; + + for (int i = 0; + i < hikari_configuration.normal_mode.nkeybindings[modifiers]; + i++) { + struct hikari_keybinding *keybinding = + &hikari_configuration.normal_mode.keybindings[modifiers][i]; + if (keybinding->modifiers == modifiers && + keybinding->keycode == event->keycode) { + keybinding->action(keybinding->arg); + return; + } + } + } + + wlr_seat_set_keyboard(hikari_server.seat, keyboard->device); + wlr_seat_keyboard_notify_key( + hikari_server.seat, event->time_msec, event->keycode, event->state); +} + +static void +normal_mode_button_handler( + struct hikari_workspace *workspace, struct wlr_event_pointer_button *event) +{ + if (hikari_server.keyboard_state.mod_pressed && + event->state == WLR_BUTTON_PRESSED) { + uint32_t modifiers = hikari_server.keyboard_state.modifiers; + + for (int i = 0; + i < hikari_configuration.normal_mode.nmousebindings[modifiers]; + i++) { + struct hikari_keybinding *mousebinding = + &hikari_configuration.normal_mode.mousebindings[modifiers][i]; + if (mousebinding->modifiers == modifiers && + mousebinding->keycode == event->button) { + mousebinding->action(mousebinding->arg); + return; + } + } + } + + wlr_seat_pointer_notify_button( + hikari_server.seat, event->time_msec, event->button, event->state); +} + +static void +button_handler(struct wl_listener *listener, void *data) +{ + struct wlr_event_pointer_button *event = data; + struct hikari_workspace *workspace = hikari_server.workspace; + + normal_mode_button_handler(workspace, event); +} + +static void +key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + struct wlr_event_keyboard_key *event = data; + + struct hikari_workspace *workspace = hikari_server.workspace; + + normal_mode_keyboard_handler(workspace, event, keyboard); +} + +#ifndef NDEBUG +static void +dump_debug(struct hikari_server *server) +{ + printf("---------------------------------------------------------------------" + "\n"); + printf("GROUPS\n"); + printf("---------------------------------------------------------------------" + "\n"); + struct hikari_group *group = NULL; + struct hikari_view *view; + wl_list_for_each ( + group, &hikari_server.visible_groups, visible_server_groups) { + printf("%s ", group->name); + view = NULL; + wl_list_for_each (view, &group->views, group_views) { + printf("%p ", view); + } + + printf("/ "); + + view = NULL; + wl_list_for_each (view, &group->visible_views, visible_group_views) { + printf("%p ", view); + } + + printf("\n"); + } + printf("---------------------------------------------------------------------" + "\n"); + printf("SHEETS\n"); + printf("---------------------------------------------------------------------" + "\n"); + struct hikari_sheet *sheets = hikari_server.workspace->sheets; + struct hikari_sheet *sheet; + for (int i = 0; i < 10; i++) { + sheet = &sheets[i]; + printf("%d ", sheet->nr); + view = NULL; + wl_list_for_each (view, &sheet->views, sheet_views) { + printf("%p ", view); + } + printf("\n"); + } + printf("---------------------------------------------------------------------" + "\n"); + if (hikari_server.workspace->sheet->layout != NULL) { + printf("-------------------------------------------------------------------" + "--\n"); + printf("LAYOUT\n"); + struct hikari_tile *tile; + wl_list_for_each ( + tile, &hikari_server.workspace->sheet->layout->tiles, layout_tiles) { + printf("%p ", tile->view); + } + printf("\n"); + printf("-------------------------------------------------------------------" + "--\n"); + } +} +#endif + +static void +modifier_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = + wl_container_of(listener, keyboard, modifiers); + + wlr_seat_set_keyboard(hikari_server.seat, keyboard->device); + + if (hikari_server.keyboard_state.mod_released) { + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + if (hikari_server_is_cycling() && focus_view != NULL) { + hikari_view_raise(focus_view); + hikari_view_center_cursor(focus_view); + } + + hikari_server_unset_cycling(); + } + + if (hikari_server.keyboard_state.mod_changed) { + hikari_server_refresh_indication(); +#ifndef NDEBUG + dump_debug(&hikari_server); +#endif + } + + wlr_seat_keyboard_notify_modifiers( + hikari_server.seat, &keyboard->device->keyboard->modifiers); +} + +static void +render(struct hikari_output *output, struct hikari_render_data *render_data) +{ + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + if (hikari_server.keyboard_state.mod_pressed && focus_view != NULL) { + struct hikari_workspace *workspace = hikari_server.workspace; + struct hikari_group *group = focus_view->group; + + struct hikari_view *view; + wl_list_for_each_reverse ( + view, &group->visible_views, visible_group_views) { + if (view != focus_view && view->output == output) { + render_data->geometry = hikari_view_border_geometry(view); + + if (hikari_group_first_view(group, workspace) == view) { + hikari_indicator_frame_render(&view->indicator_frame, + hikari_configuration.indicator_first, + render_data); + } else { + hikari_indicator_frame_render(&view->indicator_frame, + hikari_configuration.indicator_grouped, + render_data); + } + } + } + + if (focus_view->output == output) { + render_data->geometry = hikari_view_border_geometry(focus_view); + + hikari_indicator_frame_render(&focus_view->indicator_frame, + hikari_configuration.indicator_selected, + render_data); + + hikari_indicator_render(&hikari_server.indicator, render_data); + } + } +} + +static void +cancel(void) +{} + +void +hikari_normal_mode_init(struct hikari_normal_mode *normal_mode) +{ + normal_mode->mode.type = HIKARI_MODE_TYPE_NORMAL; + normal_mode->mode.key_handler = key_handler; + normal_mode->mode.button_handler = button_handler; + normal_mode->mode.modifier_handler = modifier_handler; + normal_mode->mode.render = render; + normal_mode->mode.cancel = cancel; + normal_mode->mode.cursor_move = NULL; +} diff --git a/src/output.c b/src/output.c new file mode 100644 index 0000000..fcc5ccd --- /dev/null +++ b/src/output.c @@ -0,0 +1,412 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_XWAYLAND +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_XWAYLAND +#include +#include +#endif + +static void +load_background(struct hikari_workspace *workspace, const char *path) +{ + cairo_surface_t *image; + + image = cairo_image_surface_create_from_png(path); + + int width = workspace->output->geometry.width; + int height = workspace->output->geometry.height; + + unsigned char *data = cairo_image_surface_get_data(image); + int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); + + struct wlr_renderer *renderer = + wlr_backend_get_renderer(workspace->output->output->backend); + + workspace->background = wlr_texture_from_pixels( + renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); + + cairo_surface_destroy(image); +} + +static void +render_texture(struct wlr_output *output, + pixman_region32_t *damage, + struct wlr_texture *texture, + struct wlr_renderer *renderer, + const float matrix[static 9], + struct wlr_box *box) +{ + pixman_region32_t local_damage; + pixman_region32_init(&local_damage); + pixman_region32_union_rect( + &local_damage, &local_damage, box->x, box->y, box->width, box->height); + + pixman_region32_intersect(&local_damage, &local_damage, damage); + + bool damaged = pixman_region32_not_empty(&local_damage); + if (!damaged) { + goto damage_finish; + } + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&local_damage, &nrects); + for (int i = 0; i < nrects; ++i) { + hikari_output_scissor_render(output, renderer, &rects[i]); + wlr_render_texture_with_matrix(renderer, texture, matrix, 1); + } + +damage_finish: + pixman_region32_fini(&local_damage); +} + +static void +render_surface(struct wlr_surface *surface, int sx, int sy, void *data) +{ + assert(surface != NULL); + + struct wlr_texture *texture = wlr_surface_get_texture(surface); + + if (texture == NULL) { + return; + } + + struct hikari_render_data *render_data = data; + struct wlr_box *geometry = render_data->geometry; + struct wlr_output *wlr_output = render_data->output; + + double ox = geometry->x + sx; + double oy = geometry->y + sy; + + struct wlr_box box = { .x = ox * wlr_output->scale, + .y = oy * wlr_output->scale, + .width = surface->current.width * wlr_output->scale, + .height = surface->current.height * wlr_output->scale }; + + float matrix[9]; + enum wl_output_transform transform = + wlr_output_transform_invert(surface->current.transform); + + wlr_matrix_project_box( + matrix, &box, transform, 0, wlr_output->transform_matrix); + + render_texture(wlr_output, + render_data->damage, + texture, + render_data->renderer, + matrix, + &box); +} + +static void +render_output(struct hikari_output *output, + pixman_region32_t *damage, + struct timespec *now) +{ + struct wlr_renderer *renderer = + wlr_backend_get_renderer(output->output->backend); + + struct wlr_output *wlr_output = output->output; + + wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); + + if (!pixman_region32_not_empty(damage)) { + goto render_end; + } + + float *clear_color = hikari_configuration.clear; + +#ifndef NDEBUG + if (hikari_server.track_damage) { + float damage_color[4]; + hikari_color_convert(damage_color, 0x000000); + wlr_renderer_clear(renderer, damage_color); + } +#endif + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); + for (int i = 0; i < nrects; ++i) { + hikari_output_scissor_render(wlr_output, renderer, &rects[i]); + wlr_renderer_clear(renderer, clear_color); + } + + struct hikari_render_data render_data = { + .output = wlr_output, .renderer = renderer, .when = now, .damage = damage + }; + + hikari_workspace_render_background(output->workspace, &render_data); + + struct hikari_view *view = NULL; + wl_list_for_each_reverse (view, &output->workspace->views, workspace_views) { + render_data.geometry = hikari_view_border_geometry(view); + + if (hikari_view_wants_border(view)) { + hikari_border_render(&view->border, &render_data); + } + + render_data.geometry = hikari_view_geometry(view); + + hikari_view_interface_for_each_surface( + (struct hikari_view_interface *)view, render_surface, &render_data); + } + +#ifdef HAVE_XWAYLAND + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = NULL; + wl_list_for_each_reverse (xwayland_unmanaged_view, + &output->unmanaged_xwayland_views, + unmanaged_server_views) { + + render_data.geometry = xwayland_unmanaged_view->geometry; + + wlr_surface_for_each_surface(xwayland_unmanaged_view->surface->surface, + render_surface, + &render_data); + } +#endif + + hikari_server.mode->render(output, &render_data); + +render_end: + wlr_renderer_scissor(renderer, NULL); + wlr_output_render_software_cursors(wlr_output, NULL); + wlr_renderer_end(renderer); + + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + + pixman_region32_t frame_damage; + pixman_region32_init(&frame_damage); + + enum wl_output_transform transform = + wlr_output_transform_invert(wlr_output->transform); + wlr_region_transform( + &frame_damage, &output->damage->current, transform, width, height); + + wlr_output_set_damage(wlr_output, &frame_damage); + pixman_region32_fini(&frame_damage); + + wlr_output_commit(wlr_output); +} + +static void +send_frame_done(struct wlr_surface *surface, int sx, int sy, void *data) +{ + assert(surface != NULL); + + struct timespec *now = data; + wlr_surface_send_frame_done(surface, now); +} + +static void +damage_frame_handler(struct wl_listener *listener, void *data) +{ + assert(!hikari_server.locked); + + bool needs_frame; + struct timespec now; + struct hikari_output *output = + wl_container_of(listener, output, damage_frame); + + pixman_region32_t buffer_damage; + pixman_region32_init(&buffer_damage); + + if (!wlr_output_damage_attach_render( + output->damage, &needs_frame, &buffer_damage)) { + goto buffer_damage_end; + } + + if (needs_frame) { + clock_gettime(CLOCK_MONOTONIC, &now); + render_output(output, &buffer_damage, &now); + } + +buffer_damage_end: + pixman_region32_fini(&buffer_damage); + + struct hikari_view *view = NULL; + wl_list_for_each_reverse (view, &output->workspace->views, workspace_views) { + hikari_view_interface_for_each_surface( + (struct hikari_view_interface *)view, send_frame_done, &now); + } + +#ifdef HAVE_XWAYLAND + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = NULL; + wl_list_for_each_reverse (xwayland_unmanaged_view, + &output->unmanaged_xwayland_views, + unmanaged_server_views) { + wlr_surface_for_each_surface( + xwayland_unmanaged_view->surface->surface, send_frame_done, &now); + } +#endif +} + +void +hikari_output_damage_whole(struct hikari_output *output) +{ + assert(output != NULL); + + wlr_output_damage_add_whole(output->damage); +} + +void +hikari_output_disable(struct hikari_output *output) +{ + assert(output != NULL); + + struct wlr_output *wlr_output = output->output; + + wl_list_remove(&output->damage_frame.link); + + wlr_output_rollback(wlr_output); + wlr_output_enable(wlr_output, false); + wlr_output_commit(wlr_output); +} + +void +hikari_output_enable(struct hikari_output *output) +{ + assert(output != NULL); + + output->damage_frame.notify = damage_frame_handler; + wl_signal_add(&output->damage->events.frame, &output->damage_frame); + + wlr_output_enable(output->output, true); + wlr_output_commit(output->output); + hikari_output_damage_whole(output); +} + +void +hikari_output_scissor_render(struct wlr_output *wlr_output, + struct wlr_renderer *renderer, + pixman_box32_t *rect) +{ + assert(wlr_output != NULL); + + struct wlr_box box = { .x = rect->x1, + .y = rect->y1, + .width = rect->x2 - rect->x1, + .height = rect->y2 - rect->y1 }; + + /* int ow, oh; */ + /* wlr_output_transformed_resolution(wlr_output, &ow, &oh); */ + + /* enum wl_output_transform transform = */ + /* wlr_output_transform_invert(wlr_output->transform); */ + /* wlr_box_transform(&box, &box, transform, ow, oh); */ + + wlr_renderer_scissor(renderer, &box); +} + +static void +output_geometry(struct hikari_output *output) +{ + struct wlr_box *output_box = + wlr_output_layout_get_box(hikari_server.output_layout, output->output); + + output->geometry.x = output_box->x; + output->geometry.y = output_box->y; + output->geometry.width = output_box->width; + output->geometry.height = output_box->height; +} + +/* static void */ +/* mode_handler(struct wl_listener *listener, void *data) */ +/* { */ +/* #if !defined(NDEBUG) */ +/* printf("MODE\n"); */ +/* #endif */ + +/* struct hikari_output *output = wl_container_of( */ +/* listener, */ +/* output, */ +/* mode); */ + +/* output_geometry(output); */ +/* } */ + +static void +destroy_handler(struct wl_listener *listener, void *data) +{ + struct hikari_output *output = wl_container_of(listener, output, destroy); + + hikari_output_fini(output); + hikari_free(output); +} + +void +hikari_output_init(struct hikari_output *output, struct wlr_output *wlr_output) +{ + output->output = wlr_output; + output->damage = wlr_output_damage_create(wlr_output); + +#ifdef HAVE_XWAYLAND + wl_list_init(&output->unmanaged_xwayland_views); +#endif + wl_list_init(&output->views); + + /* output->mode.notify = mode_handler; */ + /* wl_signal_add(&wlr_output->events.mode, &output->mode); */ + output->destroy.notify = destroy_handler; + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + + wlr_output_layout_add_auto(hikari_server.output_layout, wlr_output); + wlr_output_create_global(wlr_output); + + output->workspace = hikari_malloc(sizeof(struct hikari_workspace)); + hikari_workspace_init(output->workspace, output); + + wl_list_insert(&hikari_server.outputs, &output->server_outputs); + + if (!wl_list_empty(&wlr_output->modes)) { + struct wlr_output_mode *mode = + wl_container_of(wlr_output->modes.prev, mode, link); + wlr_output_set_mode(wlr_output, mode); + } + + wlr_output->data = output; + + output_geometry(output); + + char *background = hikari_configuration_resolve_background( + &hikari_configuration, wlr_output->name); + + hikari_output_enable(output); + + if (background != NULL) { + load_background(output->workspace, background); + } +} + +void +hikari_output_fini(struct hikari_output *output) +{ + wl_list_remove(&output->damage_frame.link); + /* wl_list_remove(&output->mode.link); */ + wl_list_remove(&output->destroy.link); + + hikari_workspace_fini(output->workspace); + hikari_free(output->workspace); +} diff --git a/src/pointer_config.c b/src/pointer_config.c new file mode 100644 index 0000000..e36609f --- /dev/null +++ b/src/pointer_config.c @@ -0,0 +1,9 @@ +#include + +#include + +void +hikari_pointer_config_fini(struct hikari_pointer_config *pointer_config) +{ + hikari_free(pointer_config->name); +} diff --git a/src/resize_mode.c b/src/resize_mode.c new file mode 100644 index 0000000..29c3b9c --- /dev/null +++ b/src/resize_mode.c @@ -0,0 +1,100 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +static void +cancel(void) +{ + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + if (focus_view != NULL) { + hikari_indicator_update(&hikari_server.indicator, + focus_view, + hikari_configuration.indicator_selected); + + hikari_view_center_cursor(focus_view); + } +} + +static void +key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + + struct wlr_event_keyboard_key *event = data; + + if (event->state == WLR_KEY_RELEASED) { + hikari_server_enter_normal_mode(NULL); + } +} + +static void +modifier_handler(struct wl_listener *listener, void *data) +{} + +static void +render(struct hikari_output *output, struct hikari_render_data *render_data) +{ + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + if (focus_view->output == output) { + render_data->geometry = hikari_view_border_geometry(focus_view); + + hikari_indicator_frame_render(&focus_view->indicator_frame, + hikari_configuration.indicator_insert, + render_data); + + hikari_indicator_render(&hikari_server.indicator, render_data); + } +} + +static void +cursor_move(void) +{ + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + assert(focus_view != NULL); + + struct hikari_output *output = focus_view->output; + struct wlr_box *geometry = hikari_view_geometry(focus_view); + + int cursor_x = hikari_server.cursor->x - output->geometry.x; + int cursor_y = hikari_server.cursor->y - output->geometry.y; + + int new_width = cursor_x - geometry->x - hikari_configuration.border; + int new_height = cursor_y - geometry->y - hikari_configuration.border; + + if (new_width > 0 && new_height > 0) { + hikari_view_resize_absolute(focus_view, new_width, new_height); + } +} + +static void +button_handler(struct wl_listener *listener, void *data) +{ + struct wlr_event_pointer_button *event = data; + + if (event->state == WLR_BUTTON_RELEASED) { + hikari_server_enter_normal_mode(NULL); + } +} + +void +hikari_resize_mode_init(struct hikari_resize_mode *resize_mode) +{ + resize_mode->mode.type = HIKARI_MODE_TYPE_MOVE; + resize_mode->mode.key_handler = key_handler; + resize_mode->mode.button_handler = button_handler; + resize_mode->mode.modifier_handler = modifier_handler; + resize_mode->mode.render = render; + resize_mode->mode.cancel = cancel; + resize_mode->mode.cursor_move = cursor_move; +} diff --git a/src/server.c b/src/server.c new file mode 100644 index 0000000..c89074f --- /dev/null +++ b/src/server.c @@ -0,0 +1,1033 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_XWAYLAND +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_XWAYLAND +#include +#include +#endif + +static void +add_pointer(struct hikari_server *server, struct wlr_input_device *device) +{ + struct hikari_pointer_config *pointer_config = + hikari_configuration_resolve_pointer_config( + &hikari_configuration, device->name); + + if (pointer_config != NULL) { + struct libinput_device *libinput_device = + wlr_libinput_get_device_handle(device); + + if (libinput_device != NULL) { + libinput_device_config_accel_set_speed( + libinput_device, pointer_config->accel); + + if (pointer_config->scroll_button != 0) { + libinput_device_config_scroll_set_button( + libinput_device, pointer_config->scroll_button); + } + + libinput_device_config_scroll_set_method( + libinput_device, pointer_config->scroll_method); + } + } + + wlr_cursor_attach_input_device(server->cursor, device); + wlr_cursor_map_input_to_output(server->cursor, device, NULL); +} + +static void +add_keyboard(struct hikari_server *server, struct wlr_input_device *device) +{ + struct hikari_keyboard *keyboard = + hikari_malloc(sizeof(struct hikari_keyboard)); + + hikari_keyboard_init(keyboard, device); +} + +static void +new_input_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = wl_container_of(listener, server, new_input); + + struct wlr_input_device *device = data; + + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + add_keyboard(server, device); + break; + + case WLR_INPUT_DEVICE_POINTER: + add_pointer(server, device); + break; + + default: + break; + } + + uint32_t caps = WL_SEAT_CAPABILITY_POINTER; + if (!wl_list_empty(&server->keyboards)) { + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + } + wlr_seat_set_capabilities(server->seat, caps); + + if ((caps & WL_SEAT_CAPABILITY_POINTER) != 0) { + wlr_xcursor_manager_set_cursor_image( + server->cursor_mgr, "left_ptr", server->cursor); + } +} + +static void +new_output_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = wl_container_of(listener, server, new_output); + + assert(server == &hikari_server); + + struct wlr_output *wlr_output = data; + struct hikari_output *output = hikari_malloc(sizeof(struct hikari_output)); + + hikari_output_init(output, wlr_output); + if (hikari_server.workspace == NULL) { + hikari_server.workspace = output->workspace; + hikari_server_activate_cursor(); + hikari_server.mode = (struct hikari_mode *)&hikari_server.normal_mode; + } +} + +static bool +surface_at(struct hikari_view_interface *view_interface, + double ox, + double oy, + struct wlr_surface **surface, + double *sx, + double *sy) +{ + double out_sx, out_sy; + + struct wlr_surface *out_surface = hikari_view_interface_surface_at( + view_interface, ox, oy, &out_sx, &out_sy); + + if (out_surface != NULL) { + *sx = out_sx; + *sy = out_sy; + *surface = out_surface; + return true; + } + + return false; +} + +static struct hikari_view_interface * +view_interface_at( + double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) +{ + struct wlr_output *wlr_output = + wlr_output_layout_output_at(hikari_server.output_layout, lx, ly); + + if (wlr_output == NULL) { + return NULL; + } + + struct hikari_output *output = wlr_output->data; + struct hikari_workspace *workspace; + + if (hikari_server.workspace != output->workspace) { + if (hikari_server.workspace->focus_view != NULL) { + hikari_workspace_focus_view(hikari_server.workspace, NULL); + } + + workspace = output->workspace; + hikari_server.workspace = workspace; + + if (workspace->focus_view != NULL) { + hikari_workspace_focus_view(workspace, NULL); + } + } else { + workspace = hikari_server.workspace; + } + + double ox = lx - output->geometry.x; + double oy = ly - output->geometry.y; + +#ifdef HAVE_XWAYLAND + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = NULL; + wl_list_for_each (xwayland_unmanaged_view, + &output->unmanaged_xwayland_views, + unmanaged_server_views) { + struct hikari_view_interface *view_interface = + (struct hikari_view_interface *)xwayland_unmanaged_view; + + if (surface_at(view_interface, ox, oy, surface, sx, sy)) { + return view_interface; + } + } +#endif + + struct hikari_view *view = NULL; + wl_list_for_each (view, &workspace->views, workspace_views) { + struct hikari_view_interface *view_interface = + (struct hikari_view_interface *)view; + + if (surface_at(view_interface, ox, oy, surface, sx, sy)) { + return view_interface; + } + } + + return NULL; +} + +static void +cursor_focus(uint32_t time) +{ + assert(hikari_server_in_normal_mode()); + + double sx, sy; + struct wlr_seat *seat = hikari_server.seat; + struct wlr_surface *surface = NULL; + + struct hikari_view_interface *view_interface = view_interface_at( + hikari_server.cursor->x, hikari_server.cursor->y, &surface, &sx, &sy); + + struct hikari_workspace *workspace = hikari_server.workspace; + + if (view_interface) { + struct hikari_view_interface *focus_view_interface = + (struct hikari_view_interface *)workspace->focus_view; + + if (view_interface != focus_view_interface && + view_interface->focus != NULL) { + hikari_view_interface_focus(view_interface); + } + + bool mouse_focus_changed = + hikari_server.seat->pointer_state.focused_surface != surface; + + wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + if (!mouse_focus_changed) { + wlr_seat_pointer_notify_motion(seat, time, sx, sy); + } + } else { + wlr_seat_pointer_clear_focus(seat); + } +} + +void +hikari_server_cursor_focus(void) +{ + // TODO assert and check if we are in normal mode before we call this + if (!hikari_server_in_normal_mode()) { + return; + } + + struct timespec now; + uint32_t time = (uint32_t)clock_gettime(CLOCK_MONOTONIC, &now); + cursor_focus(time); +} + +static void +cursor_button_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = + wl_container_of(listener, server, cursor_button); + + assert(!server->locked); + + hikari_server.mode->button_handler(listener, data); +} + +static void +cursor_move(uint32_t time_msec) +{ + // this is cheaper than calling a function, since this happens quite often, we + // for the common case. + if (hikari_server.mode->cursor_move == NULL) { + cursor_focus(time_msec); + } else { + hikari_server.mode->cursor_move(); + } +} + +static void +cursor_motion_absolute_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = + wl_container_of(listener, server, cursor_motion_absolute); + + assert(!server->locked); + + struct wlr_event_pointer_motion_absolute *event = data; + + wlr_cursor_warp_absolute(server->cursor, event->device, event->x, event->y); + + cursor_move(event->time_msec); +} + +static void +cursor_motion_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = + wl_container_of(listener, server, cursor_motion); + + assert(!server->locked); + + struct wlr_event_pointer_motion *event = data; + + wlr_cursor_move( + server->cursor, event->device, event->delta_x, event->delta_y); + + cursor_move(event->time_msec); +} + +static void +cursor_frame_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = + wl_container_of(listener, server, cursor_frame); + + assert(!server->locked); + + wlr_seat_pointer_notify_frame(server->seat); +} + +static void +cursor_axis_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = wl_container_of(listener, server, cursor_axis); + + assert(!server->locked); + + struct wlr_event_pointer_axis *event = data; + + wlr_seat_pointer_notify_axis(server->seat, + event->time_msec, + event->orientation, + event->delta, + event->delta_discrete, + event->source); +} + +static void +request_set_primary_selection_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = + wl_container_of(listener, server, request_set_primary_selection); + + struct wlr_seat_request_set_primary_selection_event *event = data; + + wlr_seat_set_primary_selection(server->seat, event->source, event->serial); +} + +static void +request_set_selection_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = + wl_container_of(listener, server, request_set_selection); + + struct wlr_seat_request_set_selection_event *event = data; + + wlr_seat_set_selection(server->seat, event->source, event->serial); +} + +void +hikari_server_activate_cursor(void) +{ + hikari_server.cursor_motion_absolute.notify = cursor_motion_absolute_handler; + wl_signal_add(&hikari_server.cursor->events.motion_absolute, + &hikari_server.cursor_motion_absolute); + + hikari_server.cursor_frame.notify = cursor_frame_handler; + wl_signal_add( + &hikari_server.cursor->events.frame, &hikari_server.cursor_frame); + + hikari_server.cursor_motion.notify = cursor_motion_handler; + wl_signal_add( + &hikari_server.cursor->events.motion, &hikari_server.cursor_motion); + + hikari_server.cursor_button.notify = cursor_button_handler; + wl_signal_add( + &hikari_server.cursor->events.button, &hikari_server.cursor_button); + + hikari_server.cursor_axis.notify = cursor_axis_handler; + wl_signal_add(&hikari_server.cursor->events.axis, &hikari_server.cursor_axis); +} + +void +hikari_server_deactivate_cursor(void) +{ + wl_list_remove(&hikari_server.cursor_motion_absolute.link); + wl_list_remove(&hikari_server.cursor_frame.link); + wl_list_remove(&hikari_server.cursor_motion.link); + wl_list_remove(&hikari_server.cursor_button.link); + wl_list_remove(&hikari_server.cursor_axis.link); +} + +#ifdef HAVE_XWAYLAND +static void +new_xwayland_surface_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = + wl_container_of(listener, server, new_xwayland_surface); + + struct wlr_xwayland_surface *wlr_xwayland_surface = data; + + struct hikari_workspace *workspace = server->workspace; + + if (wlr_xwayland_surface->override_redirect) { + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = + hikari_malloc(sizeof(struct hikari_xwayland_unmanaged_view)); + + hikari_xwayland_unmanaged_view_init( + xwayland_unmanaged_view, wlr_xwayland_surface, workspace); + } else { + struct hikari_xwayland_view *xwayland_view = + hikari_malloc(sizeof(struct hikari_xwayland_view)); + + hikari_xwayland_view_init(xwayland_view, wlr_xwayland_surface, workspace); + } +} + +static void +setup_xwayland(struct hikari_server *server) +{ + server->xwayland = + wlr_xwayland_create(server->display, server->compositor, true); + + server->new_xwayland_surface.notify = new_xwayland_surface_handler; + wl_signal_add( + &server->xwayland->events.new_surface, &server->new_xwayland_surface); + + setenv("DISPLAY", server->xwayland->display_name, true); +} +#endif + +static void +setup_cursor(struct hikari_server *server) +{ + server->cursor = wlr_cursor_create(); + wlr_cursor_attach_output_layout(server->cursor, server->output_layout); + + server->cursor_mgr = wlr_xcursor_manager_create("left_ptr", 24); + wlr_xcursor_manager_load(server->cursor_mgr, 1); +} + +static void +server_decoration_mode_handler(struct wl_listener *listener, void *data) +{ + struct hikari_view_decoration *decoration = + wl_container_of(listener, decoration, mode); + struct hikari_view *view = decoration->view; + + view->use_csd = decoration->wlr_decoration->mode == + WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; + + if (view->use_csd) { + view->border.state = HIKARI_BORDER_NONE; + hikari_output_damage_whole(hikari_server.workspace->output); + } +} + +static void +server_decoration_handler(struct wl_listener *listener, void *data) +{ + struct wlr_server_decoration *wlr_decoration = data; + struct hikari_view *view = + wl_container_of(wlr_decoration->surface, view, surface); + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_from_wlr_surface(wlr_decoration->surface); + struct hikari_xdg_view *xdg_view = xdg_surface->data; + + wl_signal_add(&wlr_decoration->events.mode, &xdg_view->view.decoration.mode); + xdg_view->view.decoration.mode.notify = server_decoration_mode_handler; + + xdg_view->view.decoration.wlr_decoration = wlr_decoration; + xdg_view->view.decoration.view = &xdg_view->view; +} + +static void +setup_decorations(struct hikari_server *server) +{ + server->decoration_manager = + wlr_server_decoration_manager_create(server->display); + + wlr_server_decoration_manager_set_default_mode( + server->decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); + + wl_signal_add(&server->decoration_manager->events.new_decoration, + &server->new_decoration); + server->new_decoration.notify = server_decoration_handler; +} + +static void +setup_selection(struct hikari_server *server) +{ + wlr_gtk_primary_selection_device_manager_create(server->display); + wlr_primary_selection_v1_device_manager_create(server->display); + + wlr_data_control_manager_v1_create(server->display); + wlr_primary_selection_v1_device_manager_create(server->display); + + server->seat = wlr_seat_create(server->display, "seat0"); + assert(server->seat != NULL); + + server->request_set_primary_selection.notify = + request_set_primary_selection_handler; + wl_signal_add(&server->seat->events.request_set_primary_selection, + &server->request_set_primary_selection); + + server->request_set_selection.notify = request_set_selection_handler; + wl_signal_add(&server->seat->events.request_set_selection, + &server->request_set_selection); +} + +static void +new_xdg_surface_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = + wl_container_of(listener, server, new_xdg_surface); + + struct wlr_xdg_surface *xdg_surface = data; + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + return; + } + + struct hikari_xdg_view *xdg_view = + hikari_malloc(sizeof(struct hikari_xdg_view)); + + hikari_xdg_view_init(xdg_view, xdg_surface, server->workspace); +} + +static void +setup_xdg_shell(struct hikari_server *server) +{ + server->xdg_shell = wlr_xdg_shell_create(server->display); + + server->new_xdg_surface.notify = new_xdg_surface_handler; + wl_signal_add( + &server->xdg_shell->events.new_surface, &server->new_xdg_surface); +} + +struct hikari_server hikari_server; + +static void +output_layout_change_handler(struct wl_listener *listener, void *data) +{ + struct hikari_server *server = + wl_container_of(listener, server, output_layout_change); + + struct hikari_output *output = NULL; + wl_list_for_each (output, &server->outputs, server_outputs) { + struct wlr_box *output_box = + wlr_output_layout_get_box(hikari_server.output_layout, output->output); + output->geometry.x = output_box->x; + output->geometry.y = output_box->y; + output->geometry.width = output_box->width; + output->geometry.height = output_box->height; + } +} + +static void +server_init(struct hikari_server *server) +{ +#ifndef NDEBUG + server->track_damage = false; +#endif + + hikari_configuration_init(&hikari_configuration); + + server->keyboard_state.modifiers = 0; + server->keyboard_state.mod_released = false; + server->keyboard_state.mod_changed = false; + server->keyboard_state.mod_pressed = false; + + server->locked = false; + server->cycling = false; + server->workspace = NULL; + server->display = wl_display_create(); + assert(server->display); + + hikari_indicator_init( + &server->indicator, hikari_configuration.indicator_selected); + + wl_list_init(&server->outputs); + + server->event_loop = wl_display_get_event_loop(server->display); + assert(server->event_loop); + + server->backend = wlr_backend_autocreate(server->display, NULL); + assert(server->backend); + + if (getuid() != geteuid() || getgid() != getegid()) { + if (setuid(getuid()) != 0 || setgid(getgid()) != 0) { + exit(EXIT_FAILURE); + } + } + + hikari_unlocker_init(); + + signal(SIGPIPE, SIG_IGN); + + server->renderer = wlr_backend_get_renderer(server->backend); + assert(server->renderer); + + wlr_renderer_init_wl_display(server->renderer, server->display); + + server->socket = wl_display_add_socket_auto(server->display); + if (!server->socket) { + return; + } + + setenv("WAYLAND_DISPLAY", server->socket, true); + + server->compositor = wlr_compositor_create(server->display, server->renderer); + + server->data_device_manager = wlr_data_device_manager_create(server->display); + + wl_list_init(&server->keyboards); + server->new_input.notify = new_input_handler; + wl_signal_add(&server->backend->events.new_input, &server->new_input); + + server->output_layout = wlr_output_layout_create(); + server->output_manager = + wlr_xdg_output_manager_v1_create(server->display, server->output_layout); + + server->output_layout_change.notify = output_layout_change_handler; + wl_signal_add( + &server->output_layout->events.change, &server->output_layout_change); + + server->new_output.notify = new_output_handler; + wl_signal_add(&server->backend->events.new_output, &server->new_output); + +#ifdef HAVE_XWAYLAND + setup_xwayland(server); +#endif + setup_cursor(server); + setup_decorations(server); + setup_selection(server); + setup_xdg_shell(server); + + wl_list_init(&server->groups); + wl_list_init(&server->visible_groups); + wl_list_init(&server->workspaces); + + hikari_normal_mode_init(&server->normal_mode); + hikari_exec_select_mode_init(&server->exec_select_mode); + hikari_mark_select_mode_init(&server->mark_select_mode); + hikari_mark_assign_mode_init(&server->mark_assign_mode); + hikari_group_assign_mode_init(&server->group_assign_mode); + hikari_keyboard_grab_mode_init(&server->keyboard_grab_mode); + hikari_tiling_mode_init(&server->tiling_mode); + hikari_move_mode_init(&server->move_mode); + hikari_resize_mode_init(&server->resize_mode); + + hikari_marks_init(); + hikari_execs_init(); +} + +static void +sig_handler(int signal) +{ + hikari_server_terminate(NULL); +} + +void +hikari_server_start(void) +{ + server_init(&hikari_server); + signal(SIGTERM, sig_handler); + wlr_backend_start(hikari_server.backend); + hikari_command_execute("hikari-session"); + wl_display_run(hikari_server.display); +} + +void +hikari_server_terminate(void *arg) +{ + wl_display_terminate(hikari_server.display); +} + +void +hikari_server_stop(void) +{ + wl_display_destroy_clients(hikari_server.display); + + wl_list_remove(&hikari_server.new_output.link); + wl_list_remove(&hikari_server.new_input.link); + wl_list_remove(&hikari_server.new_xdg_surface.link); + wl_list_remove(&hikari_server.cursor_motion_absolute.link); + wl_list_remove(&hikari_server.cursor_motion.link); + wl_list_remove(&hikari_server.cursor_frame.link); + wl_list_remove(&hikari_server.cursor_axis.link); + wl_list_remove(&hikari_server.cursor_button.link); + wl_list_remove(&hikari_server.request_set_primary_selection.link); + wl_list_remove(&hikari_server.output_layout_change.link); +#ifdef HAVE_XWAYLAND + wl_list_remove(&hikari_server.new_xwayland_surface.link); +#endif + + hikari_indicator_fini(&hikari_server.indicator); + hikari_mark_assign_mode_fini(&hikari_server.mark_assign_mode); + + wlr_seat_destroy(hikari_server.seat); + wlr_output_layout_destroy(hikari_server.output_layout); + + wl_display_destroy(hikari_server.display); + + hikari_configuration_fini(&hikari_configuration); + hikari_marks_fini(); + hikari_execs_fini(); +} + +struct hikari_group * +hikari_server_find_group(const char *group_name) +{ + struct hikari_group *group; + wl_list_for_each (group, &hikari_server.groups, server_groups) { + if (!strcmp(group_name, group->name)) { + return group; + } + } + + return NULL; +} + +struct hikari_group * +hikari_server_find_or_create_group(const char *group_name) +{ + struct hikari_group *group = hikari_server_find_group(group_name); + + if (group == NULL) { + group = hikari_malloc(sizeof(struct hikari_group)); + hikari_group_init(group, group_name); + group->sheet = NULL; + } + + return group; +} + +void +hikari_server_lock(void *arg) +{ + hikari_unlocker_start(); + + hikari_server.locked = true; + + hikari_server_deactivate_cursor(); + + if (hikari_server.workspace->focus_view != NULL) { + hikari_workspace_focus_view(hikari_server.workspace, NULL); + } + + wlr_seat_pointer_clear_focus(hikari_server.seat); + + struct hikari_output *output = NULL; + wl_list_for_each (output, &hikari_server.outputs, server_outputs) { + hikari_output_disable(output); + } +} + +void +hikari_server_unlock(void) +{ + hikari_server.locked = false; + + hikari_server_activate_cursor(); + + struct hikari_output *output = NULL; + wl_list_for_each (output, &hikari_server.outputs, server_outputs) { + hikari_output_enable(output); + } + + hikari_server_cursor_focus(); +} + +#define CYCLE_ACTION(n) \ + void hikari_server_cycle_##n(void *arg) \ + { \ + struct hikari_view *view; \ + \ + hikari_server_set_cycling(); \ + view = hikari_workspace_##n(hikari_server.workspace); \ + \ + if (view != NULL && view != hikari_server.workspace->focus_view) { \ + hikari_workspace_focus_view(hikari_server.workspace, view); \ + } \ + } + +CYCLE_ACTION(first_group_view) +CYCLE_ACTION(last_group_view) +CYCLE_ACTION(next_group_view) +CYCLE_ACTION(prev_group_view) +CYCLE_ACTION(next_layout_view) +CYCLE_ACTION(prev_layout_view) +CYCLE_ACTION(first_layout_view) +CYCLE_ACTION(last_layout_view) +CYCLE_ACTION(next_group) +CYCLE_ACTION(prev_group) +CYCLE_ACTION(next_sheet_view) +CYCLE_ACTION(prev_sheet_view) +#undef CYCLE_ACTION + +void +hikari_server_enter_normal_mode(void *arg) +{ + assert(hikari_server.workspace != NULL); + + hikari_server.mode->cancel(); + + hikari_server.mode = (struct hikari_mode *)&hikari_server.normal_mode; + hikari_server_refresh_indication(); +} + +void +hikari_server_enter_move_mode(void *arg) +{ + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + if (focus_view == NULL) { + return; + } + + hikari_indicator_update(&hikari_server.indicator, + focus_view, + hikari_configuration.indicator_insert); + + hikari_view_raise(focus_view); + hikari_view_top_left_cursor(focus_view); + + hikari_server.mode = (struct hikari_mode *)&hikari_server.move_mode; + hikari_server_refresh_indication(); +} + +void +hikari_server_enter_resize_mode(void *arg) +{ + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + if (focus_view == NULL) { + return; + } + + hikari_indicator_update(&hikari_server.indicator, + focus_view, + hikari_configuration.indicator_insert); + + hikari_view_raise(focus_view); + hikari_view_bottom_right_cursor(focus_view); + + hikari_server.mode = (struct hikari_mode *)&hikari_server.resize_mode; + hikari_server_refresh_indication(); +} + +void +hikari_server_enter_group_assign_mode(void *arg) +{ + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + if (focus_view == NULL) { + return; + } + + struct hikari_group_assign_mode *mode = &hikari_server.group_assign_mode; + + mode->group = focus_view->group; + hikari_server.mode = (struct hikari_mode *)mode; + + struct wlr_box *geometry = hikari_view_border_geometry(focus_view); + struct hikari_output *output = hikari_server.workspace->output; + + if (focus_view->group == focus_view->sheet->group) { + hikari_input_buffer_clear(&mode->input_buffer); + hikari_indicator_update_group(&hikari_server.indicator, + geometry, + output, + " ", + hikari_configuration.indicator_insert); + } else { + hikari_input_buffer_replace(&mode->input_buffer, focus_view->group->name); + + hikari_indicator_update_group(&hikari_server.indicator, + geometry, + output, + focus_view->group->name, + hikari_configuration.indicator_insert); + } +} + +void +hikari_server_enter_keyboard_grab_mode(void *arg) +{ + struct hikari_workspace *workspace = hikari_server.workspace; + + assert(workspace != NULL); + + if (workspace->focus_view == NULL) { + return; + } + + hikari_server.mode = (struct hikari_mode *)&hikari_server.keyboard_grab_mode; + + hikari_server_refresh_indication(); +} + +void +hikari_server_enter_tiling_mode(void *arg) +{ + struct hikari_workspace *workspace = hikari_server.workspace; + + assert(workspace != NULL); + + if (workspace->sheet->layout == NULL) { + return; + } + + if (workspace->focus_view != NULL) { + hikari_workspace_focus_view(workspace, NULL); + } + + hikari_server.mode = (struct hikari_mode *)&hikari_server.tiling_mode; + + hikari_output_damage_whole(workspace->output); +} + +void +hikari_server_enter_mark_select_mode(void *arg) +{ + assert(hikari_server.workspace != NULL); + + hikari_server.mark_select_mode.switch_workspace = false; + + hikari_server.mode = (struct hikari_mode *)&hikari_server.mark_select_mode; + + hikari_server_refresh_indication(); +} + +void +hikari_server_enter_exec_select_mode(void *arg) +{ + assert(hikari_server.workspace != NULL); + + hikari_server.mode = (struct hikari_mode *)&hikari_server.exec_select_mode; + + hikari_server_refresh_indication(); +} + +void +hikari_server_enter_mark_select_switch_mode(void *arg) +{ + hikari_server.mark_select_mode.switch_workspace = true; + + hikari_server.mode = (struct hikari_mode *)&hikari_server.mark_select_mode; + + hikari_server_refresh_indication(); +} + +void +hikari_server_enter_mark_assign_mode(void *arg) +{ + assert(hikari_server.workspace != NULL); + + struct hikari_workspace *workspace = hikari_server.workspace; + + if (workspace->focus_view == NULL) { + return; + } + + hikari_server.mark_assign_mode.pending_mark = NULL; + hikari_server.mode = (struct hikari_mode *)&hikari_server.mark_assign_mode; + + struct wlr_box *geometry = hikari_view_border_geometry(workspace->focus_view); + struct hikari_output *output = hikari_server.workspace->output; + + struct hikari_mark *mark = workspace->focus_view->mark; + + if (mark == NULL) { + hikari_indicator_update_mark(&hikari_server.indicator, + geometry, + output, + " ", + hikari_configuration.indicator_insert); + } else { + hikari_indicator_update_mark(&hikari_server.indicator, + geometry, + output, + mark->name, + hikari_configuration.indicator_insert); + } + + hikari_server_refresh_indication(); +} + +void +hikari_server_refresh_indication(void) +{ + struct hikari_view *view = NULL; + struct hikari_workspace *workspace = NULL; + + wl_list_for_each (workspace, &hikari_server.workspaces, server_workspaces) { + wl_list_for_each (view, &workspace->views, workspace_views) { + hikari_view_damage_whole(view); + } + } + + struct hikari_view *focus_view = hikari_server.workspace->focus_view; + + if (focus_view != NULL) { + hikari_indicator_damage(&hikari_server.indicator, focus_view); + } +} + +void +hikari_server_execute_command(void *arg) +{ + const char *command = arg; + hikari_command_execute(command); +} + +void +hikari_server_reset_sheet_layout(void *arg) +{ + hikari_sheet_reset_layout(hikari_server.workspace->sheet); +} + +void +hikari_server_layout_sheet(void *arg) +{ + struct hikari_split *split = arg; + hikari_sheet_apply_split(hikari_server.workspace->sheet, split); +} diff --git a/src/sheet.c b/src/sheet.c new file mode 100644 index 0000000..047b3c2 --- /dev/null +++ b/src/sheet.c @@ -0,0 +1,415 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +void +hikari_sheet_init( + struct hikari_sheet *sheet, int nr, struct hikari_workspace *workspace) +{ + char sheet_name[2]; + + wl_list_init(&sheet->views); + + sheet->nr = nr; + sprintf(sheet_name, "%d", nr); + + sheet->group = hikari_malloc(sizeof(struct hikari_group)); + hikari_group_init(sheet->group, sheet_name); + sheet->group->sheet = sheet; + sheet->workspace = workspace; + sheet->layout = NULL; +} + +void +hikari_sheet_fini(struct hikari_sheet *sheet) +{ + hikari_group_fini(sheet->group); + hikari_free(sheet->group); +} + +static struct hikari_view * +scan_next_tileable_view(struct hikari_view *view) +{ + assert(view != NULL); + + struct wl_list *next = view->sheet_views.next; + + while (next != &view->sheet->views) { + view = wl_container_of(next, view, sheet_views); + if (hikari_view_is_tileable(view)) { + return view; + } + next = view->sheet_views.next; + } + + return NULL; +} + +struct hikari_view * +hikari_sheet_first_tileable_view(struct hikari_sheet *sheet) +{ + assert(sheet != NULL); + + struct wl_list *next = sheet->views.next; + struct hikari_view *view = NULL; + + while (next != &sheet->views) { + view = wl_container_of(next, view, sheet_views); + if (hikari_view_is_tileable(view)) { + return view; + } + next = view->sheet_views.next; + } + + return NULL; +} + +static int +tileable_views(struct hikari_view *view) +{ + int result; + if (hikari_view_is_tileable(view)) { + result = 1; + } else { + result = 0; + } + + struct wl_list *next = view->sheet_views.next; + + while (next != &view->sheet->views) { + view = wl_container_of(next, view, sheet_views); + if (hikari_view_is_tileable(view)) { + result++; + } + next = view->sheet_views.next; + } + + return result; +} + +static struct hikari_view * +single_layout(struct wlr_box *frame, struct hikari_view *first, int nr_of_views) +{ + hikari_view_tile(first, frame); + return scan_next_tileable_view(first); +} + +static struct hikari_view * +full_layout(struct wlr_box *frame, struct hikari_view *first, int nr_of_views) +{ + struct hikari_view *view = first; + for (int i = 0; i < nr_of_views && view != NULL; i++) { + if (hikari_view_is_tileable(view)) { + hikari_view_tile(view, frame); + } + view = scan_next_tileable_view(view); + } + + return view; +} + +#define LAYOUT_VIEWS(nr_of_views, view, frame) \ + if (nr_of_views == 0) { \ + return NULL; \ + } else if (nr_of_views == 1) { \ + hikari_view_tile(view, frame); \ + } else + +static struct hikari_view * +grid_layout(struct wlr_box *frame, struct hikari_view *first, int nr_of_views) +{ + int nr_of_rows = 1; + int nr_of_cols = 1; + + for (int i = 1; i <= nr_of_views; i++) { + if (i > nr_of_rows * nr_of_cols) { + if (nr_of_cols > nr_of_rows) { + assert(nr_of_cols == nr_of_rows + 1); + nr_of_rows++; + } else { + nr_of_cols++; + } + } + } + + struct hikari_view *view = first; + LAYOUT_VIEWS(nr_of_views, first, frame) + { + int border = 2 * hikari_configuration.border; + int row_gaps = nr_of_rows - 1; + int col_gaps = nr_of_cols - 1; + int gaps_height = hikari_configuration.gap * row_gaps; + int gaps_width = hikari_configuration.gap * col_gaps; + int views_height = frame->height - border * nr_of_rows - gaps_height; + int views_width = frame->width - border * nr_of_cols - gaps_width; + + int width = views_width / nr_of_cols; + int height = views_height / nr_of_rows; + + int rest_width = + frame->width - border * col_gaps - gaps_width - width * nr_of_cols; + + int rest_height = + frame->height - border * row_gaps - gaps_height - height * nr_of_rows; + + struct wlr_box geometry = { .y = frame->y, .x = frame->x }; + + geometry.height = height + rest_height; + for (int g_y = 0; g_y < nr_of_rows; g_y++) { + if (g_y == 1) { + geometry.height = height; + } + geometry.width = width + rest_width; + for (int g_x = 0; g_x < nr_of_cols; g_x++) { + if (g_x == 1) { + geometry.width = width; + } + hikari_view_tile(view, &geometry); + + view = scan_next_tileable_view(view); + if (view == NULL) { + return NULL; + } + + geometry.x += hikari_configuration.gap + border + geometry.width; + } + geometry.x = frame->x; + geometry.y += hikari_configuration.gap + border + geometry.height; + } + } + + return scan_next_tileable_view(view); +} + +#define SPLIT_LAYOUT(name, x, y, width, height) \ + static struct hikari_view *name##_layout( \ + struct wlr_box *frame, struct hikari_view *first, int nr_of_views) \ + { \ + struct hikari_view *view = first; \ + int border = 2 * hikari_configuration.border; \ + int gaps = nr_of_views - 1; \ + int gaps_##width = hikari_configuration.gap * gaps; \ + \ + LAYOUT_VIEWS(nr_of_views, first, frame) \ + { \ + int views_width = frame->width - border * gaps - gaps_##width; \ + int width = views_width / nr_of_views; \ + int rest = views_width - width * nr_of_views; \ + \ + struct wlr_box geometry = { .x = frame->x, \ + .y = frame->y, \ + .width = width + rest, \ + .height = frame->height }; \ + \ + hikari_view_tile(first, &geometry); \ + \ + geometry.x += hikari_configuration.gap + border + width + rest; \ + geometry.width = width; \ + for (int n = 1; n < nr_of_views; n++) { \ + view = scan_next_tileable_view(view); \ + hikari_view_tile(view, &geometry); \ + geometry.x += hikari_configuration.gap + border + width; \ + } \ + } \ + \ + return scan_next_tileable_view(view); \ + } + +SPLIT_LAYOUT(vertical, x, y, width, height) +SPLIT_LAYOUT(horizontal, y, x, height, width) +#undef SPLIT_LAYOUT + +#define LAYOUT(name) \ + struct hikari_view *hikari_sheet_##name##_layout(struct hikari_sheet *sheet, \ + struct hikari_view *first, \ + struct wlr_box *frame, \ + int max) \ + { \ + int nr_of_views = tileable_views(first); \ + if (nr_of_views > max) { \ + nr_of_views = max; \ + } \ + \ + if (nr_of_views == 0) { \ + return NULL; \ + } \ + \ + return name##_layout(frame, first, nr_of_views); \ + } + +LAYOUT(vertical) +LAYOUT(horizontal) +LAYOUT(grid) +LAYOUT(full) +LAYOUT(single) +#undef LAYOUT + +#undef LAYOUT_WINDOWS + +void +hikari_sheet_reset_layout(struct hikari_sheet *sheet) +{ + struct hikari_layout *layout = sheet->layout; + + if (layout == NULL) { + return; + } + + struct hikari_view *view; + wl_list_for_each (view, &sheet->views, sheet_views) { + if (hikari_view_is_tiled(view)) { + hikari_view_reset_geometry(view); + } + } +} + +#define SHEET_VIEW(name, link) \ + struct hikari_view *hikari_sheet_##name##_view(struct hikari_sheet *sheet) \ + { \ + struct wl_list *link = sheet->views.link; \ + struct hikari_view *view; \ + \ + while (link != &sheet->views) { \ + view = wl_container_of(link, view, sheet_views); \ + if (!hikari_view_is_hidden(view)) { \ + return view; \ + } \ + link = view->sheet_views.link; \ + } \ + \ + return NULL; \ + } + +SHEET_VIEW(first, next) +SHEET_VIEW(last, prev) +#undef SHEET_VIEW + +#define SHEET_VIEW(link, fallback) \ + struct hikari_view *hikari_sheet_##link##_view( \ + struct hikari_sheet *sheet, struct hikari_view *view) \ + { \ + struct wl_list *link = view->sheet_views.link; \ + \ + while (link != &view->sheet->views) { \ + view = wl_container_of(link, view, sheet_views); \ + if (!hikari_view_is_hidden(view)) { \ + return view; \ + } \ + link = view->sheet_views.link; \ + } \ + \ + return hikari_sheet_##fallback##_view(sheet); \ + } + +SHEET_VIEW(next, first) +SHEET_VIEW(prev, last) +#undef SHEET_VIEW + +int +hikari_sheet_tileable_views(struct hikari_sheet *sheet) +{ + int nr_of_views = 0; + + struct hikari_view *view; + wl_list_for_each (view, &sheet->views, sheet_views) { + if (hikari_view_is_tileable(view)) { + nr_of_views++; + } + } + + return nr_of_views; +} + +struct hikari_sheet * +hikari_sheet_next(struct hikari_sheet *sheet) +{ + struct hikari_sheet *sheets = sheet->workspace->sheets; + + if (sheet->nr == 9) { + return &sheets[1]; + } + + return &sheets[sheet->nr + 1]; +} + +struct hikari_sheet * +hikari_sheet_prev(struct hikari_sheet *sheet) +{ + struct hikari_sheet *sheets = sheet->workspace->sheets; + + if (sheet->nr == 0 || sheet->nr == 1) { + return &sheets[9]; + } + + return &sheets[sheet->nr - 1]; +} + +struct hikari_sheet * +hikari_sheet_next_inhabited(struct hikari_sheet *sheet) +{ + struct hikari_sheet *sheets = sheet->workspace->sheets; + + if (sheet->nr == 0 || sheet->nr == 9) { + return sheet; + } + + for (uint8_t i = sheet->nr + 1; i < HIKARI_NR_OF_SHEETS; i++) { + if (!wl_list_empty(&sheets[i].views)) { + return &sheets[i]; + } + } + + return sheet; +} + +struct hikari_sheet * +hikari_sheet_prev_inhabited(struct hikari_sheet *sheet) +{ + struct hikari_sheet *sheets = sheet->workspace->sheets; + + if (sheet->nr <= 1) { + return sheet; + } + + for (uint8_t i = sheet->nr - 1; i > 0; i--) { + if (!wl_list_empty(&sheets[i].views)) { + return &sheets[i]; + } + } + + return sheet; +} + +void +hikari_sheet_apply_split(struct hikari_sheet *sheet, struct hikari_split *split) +{ + if (sheet->layout != NULL) { + struct hikari_tile *tile; + wl_list_for_each (tile, &sheet->layout->tiles, layout_tiles) { + if (hikari_view_is_dirty(tile->view)) { + return; + } + } + } else { + sheet->layout = hikari_malloc(sizeof(struct hikari_layout)); + hikari_layout_init(sheet->layout, split); + } + + struct wlr_box geometry = { .x = 0, .y = 0 }; + struct hikari_view *first = hikari_sheet_first_tileable_view(sheet); + + wlr_output_effective_resolution( + sheet->workspace->output->output, &geometry.width, &geometry.height); + + sheet->layout->split = split; + + hikari_split_apply(split, &geometry, first); +} diff --git a/src/split.c b/src/split.c new file mode 100644 index 0000000..fa1afe1 --- /dev/null +++ b/src/split.c @@ -0,0 +1,219 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include + +static struct hikari_view * +apply_split(struct hikari_split *split, + struct wlr_box *geometry, + struct hikari_view *first) +{ + if (first == NULL) { + return NULL; + } + + struct hikari_view *view = first; + switch (split->type) { + case HIKARI_SPLIT_TYPE_VERTICAL: { + struct wlr_box left, right; + struct hikari_vertical_split *vertical_split = + (struct hikari_vertical_split *)split; + + hikari_geometry_split_vertical(geometry, + vertical_split->factor, + hikari_configuration.gap + hikari_configuration.border * 2, + &left, + &right); + + view = apply_split(vertical_split->left, &left, view); + view = apply_split(vertical_split->right, &right, view); + } break; + + case HIKARI_SPLIT_TYPE_HORIZONTAL: { + struct wlr_box top, bottom; + struct hikari_horizontal_split *horizontal_split = + (struct hikari_horizontal_split *)split; + + hikari_geometry_split_horizontal(geometry, + horizontal_split->factor, + hikari_configuration.gap + hikari_configuration.border * 2, + &top, + &bottom); + + view = apply_split(horizontal_split->top, &top, view); + view = apply_split(horizontal_split->bottom, &bottom, view); + } break; + + case HIKARI_SPLIT_TYPE_CONTAINER: { + struct hikari_container *container = (struct hikari_container *)split; + container->geometry = *geometry; + view = container->layout(view->sheet, view, geometry, container->max); + } break; + } + + return view; +} + +void +hikari_split_apply(struct hikari_split *split, + struct wlr_box *geometry, + struct hikari_view *first) +{ + if (first == NULL) { + return; + } + + hikari_geometry_shrink( + geometry, hikari_configuration.gap + hikari_configuration.border); + + apply_split(split, geometry, first); +} + +void +hikari_container_init( + struct hikari_container *container, int nr_of_views, layout_func_t layout) +{ + container->split.type = HIKARI_SPLIT_TYPE_CONTAINER; + container->max = nr_of_views; + container->layout = layout; + container->geometry = (struct wlr_box){ 0 }; +} + +void +hikari_vertical_split_init(struct hikari_vertical_split *vertical_split, + float factor, + struct hikari_split *left, + struct hikari_split *right) +{ + vertical_split->split.type = HIKARI_SPLIT_TYPE_VERTICAL; + vertical_split->factor = factor; + vertical_split->left = left; + vertical_split->right = right; +} + +void +hikari_horizontal_split_init(struct hikari_horizontal_split *horizontal_split, + float factor, + struct hikari_split *top, + struct hikari_split *bottom) +{ + horizontal_split->split.type = HIKARI_SPLIT_TYPE_HORIZONTAL; + horizontal_split->factor = factor; + horizontal_split->top = top; + horizontal_split->bottom = bottom; +} + +static void +rect_render(float color[static 4], + struct wlr_box *box, + struct hikari_render_data *render_data) +{ + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_union_rect( + &damage, &damage, box->x, box->y, box->width, box->height); + + pixman_region32_intersect(&damage, &damage, render_data->damage); + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto buffer_damage_finish; + } + + struct wlr_renderer *renderer = render_data->renderer; + assert(renderer); + + float matrix[9]; + wlr_matrix_project_box(matrix, + box, + WL_OUTPUT_TRANSFORM_NORMAL, + 0, + render_data->output->transform_matrix); + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; i++) { + hikari_output_scissor_render(render_data->output, renderer, &rects[i]); + wlr_render_quad_with_matrix(renderer, color, matrix); + } + +buffer_damage_finish: + pixman_region32_fini(&damage); +} + +void +hikari_split_fini(struct hikari_split *split) +{ + switch (split->type) { + case HIKARI_SPLIT_TYPE_VERTICAL: { + struct hikari_vertical_split *vertical_split = + (struct hikari_vertical_split *)split; + + hikari_split_fini(vertical_split->left); + hikari_split_fini(vertical_split->right); + hikari_free(vertical_split); + } break; + + case HIKARI_SPLIT_TYPE_HORIZONTAL: { + struct hikari_horizontal_split *horizontal_split = + (struct hikari_horizontal_split *)split; + + hikari_split_fini(horizontal_split->top); + hikari_split_fini(horizontal_split->bottom); + hikari_free(horizontal_split); + } break; + + case HIKARI_SPLIT_TYPE_CONTAINER: { + struct hikari_container *container = (struct hikari_container *)split; + + hikari_free(container); + } break; + } +} + +void +hikari_split_render( + struct hikari_split *split, struct hikari_render_data *render_data) +{ + switch (split->type) { + case HIKARI_SPLIT_TYPE_VERTICAL: { + struct hikari_vertical_split *vertical_split = + (struct hikari_vertical_split *)split; + + hikari_split_render(vertical_split->left, render_data); + hikari_split_render(vertical_split->right, render_data); + } break; + + case HIKARI_SPLIT_TYPE_HORIZONTAL: { + struct hikari_horizontal_split *horizontal_split = + (struct hikari_horizontal_split *)split; + + hikari_split_render(horizontal_split->top, render_data); + hikari_split_render(horizontal_split->bottom, render_data); + } break; + + case HIKARI_SPLIT_TYPE_CONTAINER: { + struct hikari_container *container = (struct hikari_container *)split; + + hikari_container_render(container, render_data); + } break; + } +} + +void +hikari_container_render( + struct hikari_container *container, struct hikari_render_data *render_data) +{ + float color[4]; + hikari_color_convert(color, 0xAE81FF); + color[3] = 0.5; + + render_data->geometry = &container->geometry; + + rect_render(color, &container->geometry, render_data); +} diff --git a/src/tile.c b/src/tile.c new file mode 100644 index 0000000..3f88f30 --- /dev/null +++ b/src/tile.c @@ -0,0 +1,54 @@ +#include + +#include + +#include +#include +#include + +void +hikari_tile_init(struct hikari_tile *tile, + struct hikari_view *view, + struct wlr_box *tile_geometry, + struct wlr_box *view_geometry) +{ + tile->view = view; + tile->tile_geometry = *tile_geometry; + tile->view_geometry = *view_geometry; +} + +void +hikari_tile_fini(struct hikari_tile *tile) +{ + assert(tile->view->tile == NULL); + wl_list_remove(&tile->layout_tiles); +} + +#define CYCLE_LAYOUT(link) \ + struct hikari_view *hikari_tile_##link##_view(struct hikari_tile *tile) \ + { \ + assert(!hikari_view_is_hidden(tile->view)); \ + \ + struct wl_list *link = tile->layout_tiles.link; \ + struct hikari_tile *link##_tile; \ + struct wl_list *tiles = &hikari_server.workspace->sheet->layout->tiles; \ + \ + do { \ + if (link == tiles) { \ + link##_tile = wl_container_of(tiles->link, link##_tile, layout_tiles); \ + } else { \ + link##_tile = wl_container_of(link, link##_tile, layout_tiles); \ + } \ + link = link->link; \ + } while (hikari_view_is_hidden(link##_tile->view)); \ + \ + assert(link##_tile != NULL); \ + assert(link##_tile->view != NULL); \ + assert(!hikari_view_is_hidden(link##_tile->view)); \ + \ + return link##_tile->view; \ + } + +CYCLE_LAYOUT(next) +CYCLE_LAYOUT(prev) +#undef CYCLE_LAYOUT diff --git a/src/tiling_mode.c b/src/tiling_mode.c new file mode 100644 index 0000000..b30dab5 --- /dev/null +++ b/src/tiling_mode.c @@ -0,0 +1,104 @@ +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +tiling_key_handler(struct hikari_workspace *workspace, + struct wlr_event_keyboard_key *event, + struct hikari_keyboard *keyboard) +{ + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); + + if ((modifiers & WLR_MODIFIER_LOGO) && event->state == WLR_KEY_PRESSED) { + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_space: + if (modifiers == WLR_MODIFIER_LOGO) { + hikari_server_enter_normal_mode(NULL); + hikari_output_damage_whole(workspace->output); + hikari_server_cursor_focus(); + return; + } + } + } + } + + wlr_seat_set_keyboard(hikari_server.seat, keyboard->device); + wlr_seat_keyboard_notify_key( + hikari_server.seat, event->time_msec, event->keycode, event->state); +} + +static void +render(struct hikari_output *output, struct hikari_render_data *render_data) +{ + struct hikari_workspace *workspace = hikari_server.workspace; + + assert(workspace->sheet->layout->split != NULL); + hikari_split_render(workspace->sheet->layout->split, render_data); +} + +static void +modifier_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = + wl_container_of(listener, keyboard, modifiers); + + wlr_seat_keyboard_notify_modifiers( + hikari_server.seat, &keyboard->device->keyboard->modifiers); +} + +static void +key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + + struct wlr_event_keyboard_key *event = data; + struct hikari_workspace *workspace = hikari_server.workspace; + + tiling_key_handler(workspace, event, keyboard); +} + +static void +cancel(void) +{} + +static void +button_handler(struct wl_listener *listener, void *data) +{} + +static void +cursor_move(void) +{} + +void +hikari_tiling_mode_init(struct hikari_tiling_mode *tiling_mode) +{ + tiling_mode->mode.type = HIKARI_MODE_TYPE_TILING; + tiling_mode->mode.key_handler = key_handler; + tiling_mode->mode.button_handler = button_handler; + tiling_mode->mode.modifier_handler = modifier_handler; + tiling_mode->mode.render = render; + tiling_mode->mode.cancel = cancel; + tiling_mode->mode.cursor_move = cursor_move; +} diff --git a/src/unlocker.c b/src/unlocker.c new file mode 100644 index 0000000..d2421a0 --- /dev/null +++ b/src/unlocker.c @@ -0,0 +1,153 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +static const size_t BUFFER_SIZE = 1024; + +static char input_buffer[BUFFER_SIZE]; +static int cursor = 0; +static int locker_pipe[2][2] = { { -1, -1 }, { -1, -1 } }; + +void +hikari_unlocker_init(void) +{ + mlock(input_buffer, BUFFER_SIZE); + bzero(input_buffer, BUFFER_SIZE); +} + +void +hikari_unlocker_start(void) +{ + cursor = 0; + bzero(input_buffer, BUFFER_SIZE); + + pipe(locker_pipe[0]); + pipe(locker_pipe[1]); + + pid_t locker = fork(); + + if (locker == 0) { + close(locker_pipe[0][1]); + close(locker_pipe[1][0]); + close(0); + close(1); + dup2(locker_pipe[0][0], 0); + dup2(locker_pipe[1][1], 1); + execl("/bin/sh", "/bin/sh", "-c", "hikari-unlocker", NULL); + exit(0); + } else { + close(locker_pipe[0][0]); + close(locker_pipe[1][1]); + } +} + +static void +put_char(uint32_t codepoint) +{ + size_t length = utf8_chsize(codepoint); + + if (cursor + length < BUFFER_SIZE) { + utf8_encode(&input_buffer[cursor], length, codepoint); + cursor += length; + } +} + +static void +delete_char(void) +{ + if (cursor == 0) { + return; + } + + input_buffer[--cursor] = '\0'; +} + +static void +submit_password(void) +{ + size_t password_length = strnlen(input_buffer, 1023) + 1; + bool success = false; + + write(locker_pipe[0][1], input_buffer, password_length); + bzero(input_buffer, BUFFER_SIZE); + cursor = 0; + read(locker_pipe[1][0], &success, sizeof(bool)); + + if (success) { + int status; + hikari_server_unlock(); + close(locker_pipe[1][0]); + close(locker_pipe[0][1]); + wait(&status); + } +} + +void +hikari_unlocker_key_handler(struct wl_listener *listener, void *data) +{ + struct hikari_keyboard *keyboard = wl_container_of(listener, keyboard, key); + struct wlr_event_keyboard_key *event = data; + + if (event->state == WLR_KEY_PRESSED) { + const xkb_keysym_t *syms; + uint32_t keycode = event->keycode + 8; + uint32_t codepoint; + + int nsyms = xkb_state_key_get_syms( + keyboard->device->keyboard->xkb_state, keycode, &syms); + + for (int i = 0; i < nsyms; i++) { + switch (syms[i]) { + case XKB_KEY_Caps_Lock: + case XKB_KEY_Shift_L: + case XKB_KEY_Shift_R: + case XKB_KEY_Control_L: + case XKB_KEY_Control_R: + case XKB_KEY_Meta_L: + case XKB_KEY_Meta_R: + case XKB_KEY_Alt_L: + case XKB_KEY_Alt_R: + case XKB_KEY_Super_L: + case XKB_KEY_Super_R: + break; + + case XKB_KEY_BackSpace: + delete_char(); + break; + + case XKB_KEY_Return: + submit_password(); + break; + + default: + codepoint = xkb_state_key_get_utf32( + keyboard->device->keyboard->xkb_state, keycode); + + if (codepoint) { + put_char(codepoint); + } + break; + } + } + } +} + +void +hikari_unlocker_fini(void) +{ + munlock(input_buffer, BUFFER_SIZE); +} diff --git a/src/view.c b/src/view.c new file mode 100644 index 0000000..b0ff3f7 --- /dev/null +++ b/src/view.c @@ -0,0 +1,1527 @@ +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +move_to_top(struct hikari_view *view) +{ + assert(view != NULL); + + wl_list_remove(&view->sheet_views); + wl_list_insert(&view->sheet->views, &view->sheet_views); + + wl_list_remove(&view->group_views); + wl_list_insert(&view->group->views, &view->group_views); + + wl_list_remove(&view->output_views); + wl_list_insert(&view->output->views, &view->output_views); +} + +static inline void +place_visibly_above(struct hikari_view *view) +{ + wl_list_remove(&view->visible_group_views); + wl_list_insert(&view->group->visible_views, &view->visible_group_views); + + wl_list_remove(&view->group->visible_server_groups); + wl_list_insert( + &hikari_server.visible_groups, &view->group->visible_server_groups); + + wl_list_remove(&view->workspace_views); + wl_list_insert(&view->sheet->workspace->views, &view->workspace_views); +} + +static void +increase_group_visiblity(struct hikari_view *view) +{ + assert(!hikari_view_is_hidden(view)); + + struct hikari_group *group = view->group; + + if (wl_list_empty(&group->visible_views)) { + wl_list_insert( + &hikari_server.visible_groups, &group->visible_server_groups); + } + + wl_list_insert(&group->visible_views, &view->visible_group_views); +} + +static void +decrease_group_visibility(struct hikari_view *view) +{ + struct hikari_group *group = view->group; + + wl_list_remove(&view->visible_group_views); + + if (wl_list_empty(&group->visible_views)) { + wl_list_remove(&group->visible_server_groups); + } +} + +static void +detach_from_group(struct hikari_view *view) +{ + struct hikari_group *group = view->group; + + wl_list_remove(&view->group_views); + + if (wl_list_empty(&group->views)) { + hikari_group_fini(group); + hikari_free(group); + } +} + +static void +remove_from_group(struct hikari_view *view) +{ + assert(!hikari_view_is_hidden(view)); + assert(!hikari_group_is_sheet(view->group)); + + decrease_group_visibility(view); + detach_from_group(view); +} + +static void +remove_from_sheet_group(struct hikari_view *view) +{ + assert(hikari_group_is_sheet(view->group)); + + decrease_group_visibility(view); + + wl_list_remove(&view->group_views); + wl_list_remove(&view->sheet_views); +} + +static void +hide(struct hikari_view *view) +{ + assert(view != NULL); + assert(!hikari_view_is_hidden(view)); + +#if !defined(NDEBUG) + printf("HIDE %p\n", view); +#endif + + if (hikari_view_has_focus(view)) { + hikari_indicator_damage(&hikari_server.indicator, view); + hikari_workspace_focus_view(view->sheet->workspace, NULL); + } + + wl_list_remove(&view->workspace_views); + + view->hide(view); + + decrease_group_visibility(view); + + hikari_view_set_hidden(view); +} + +static void +regroup_from_sheet_to_sheet( + struct hikari_view *view, struct hikari_group *group) +{ + assert(hikari_group_is_sheet(view->group)); + assert(hikari_group_is_sheet(group)); + + struct hikari_sheet *sheets = view->sheet->workspace->sheets; + + if (group->sheet == view->sheet->workspace->sheet || + group->sheet == &sheets[0]) { + remove_from_sheet_group(view); + + view->sheet = group->sheet; + wl_list_insert(&view->sheet->views, &view->sheet_views); + + view->group = group; + wl_list_insert(&group->views, &view->group_views); + + increase_group_visiblity(view); + + wl_list_remove(&view->workspace_views); + wl_list_insert(&view->sheet->workspace->views, &view->workspace_views); + } else { + assert(view != NULL); + assert(hikari_group_is_sheet(group)); + + hide(view); + + view->sheet = group->sheet; + wl_list_remove(&view->sheet_views); + wl_list_insert(&view->sheet->views, &view->sheet_views); + + view->group = group; + wl_list_remove(&view->group_views); + wl_list_insert(&view->group->views, &view->group_views); + } +} + +static void +regroup_from_sheet_to_group( + struct hikari_view *view, struct hikari_group *group) +{ + assert(hikari_group_is_sheet(view->group)); + assert(!hikari_group_is_sheet(group)); + + remove_from_sheet_group(view); + + wl_list_insert(&view->sheet->views, &view->sheet_views); + + view->group = group; + wl_list_insert(&group->views, &view->group_views); + + increase_group_visiblity(view); + + wl_list_remove(&view->workspace_views); + wl_list_insert(&view->sheet->workspace->views, &view->workspace_views); +} + +static void +regroup_from_group_to_sheet( + struct hikari_view *view, struct hikari_group *group) +{ + assert(!hikari_group_is_sheet(view->group)); + assert(hikari_group_is_sheet(group)); + + struct hikari_sheet *sheets = view->sheet->workspace->sheets; + + if (group->sheet == view->sheet->workspace->sheet || + group->sheet == &sheets[0]) { + remove_from_group(view); + + view->sheet = group->sheet; + wl_list_remove(&view->sheet_views); + wl_list_insert(&view->sheet->views, &view->sheet_views); + + view->group = group; + wl_list_insert(&group->views, &view->group_views); + + increase_group_visiblity(view); + + wl_list_remove(&view->workspace_views); + wl_list_insert(&view->sheet->workspace->views, &view->workspace_views); + } else { + hide(view); + + detach_from_group(view); + + view->sheet = group->sheet; + view->group = group; + + wl_list_remove(&view->sheet_views); + wl_list_insert(&view->sheet->views, &view->sheet_views); + + wl_list_insert(&view->group->views, &view->group_views); + } +} + +static void +regroup_from_group_to_group( + struct hikari_view *view, struct hikari_group *group) +{ + assert(!hikari_group_is_sheet(view->group)); + assert(!hikari_group_is_sheet(group)); + + remove_from_group(view); + + wl_list_remove(&view->sheet_views); + wl_list_insert(&view->sheet->views, &view->sheet_views); + + view->group = group; + wl_list_insert(&group->views, &view->group_views); + + increase_group_visiblity(view); + + wl_list_remove(&view->workspace_views); + wl_list_insert(&view->sheet->workspace->views, &view->workspace_views); +} + +static void +regroup(struct hikari_view *view, struct hikari_group *group) +{ + assert(view != NULL); + assert(group != NULL); + + if (hikari_group_is_sheet(view->group)) { + if (hikari_group_is_sheet(group)) { + regroup_from_sheet_to_sheet(view, group); + } else { + regroup_from_sheet_to_group(view, group); + } + } else { + if (hikari_group_is_sheet(group)) { + regroup_from_group_to_sheet(view, group); + } else { + regroup_from_group_to_group(view, group); + } + } +} + +static inline void +refresh_border_geometry(struct hikari_view *view) +{ + assert(view != NULL); + hikari_border_refresh_geometry(&view->border, view->current_geometry); + hikari_indicator_frame_refresh_geometry(&view->indicator_frame, view); +} + +static void +move_view(struct hikari_view *view, struct wlr_box *geometry, int x, int y) +{ + if (view->maximized_state != NULL) { + switch (view->maximized_state->maximization) { + case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED: + return; + + case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED: + if (y != 0) { + return; + } + break; + + case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED: + if (x != 0) { + return; + } + break; + } + } + + int screen_width, screen_height; + wlr_output_effective_resolution( + view->output->output, &screen_width, &screen_height); + + int constrained_x = x, constrained_y = y; + + if (constrained_x != 0) { + if (constrained_x > screen_width - 10) { + constrained_x = screen_width - 10; + } else if (constrained_x + geometry->width + + hikari_configuration.border * 2 < + 10) { + constrained_x = -geometry->width - 2 * hikari_configuration.border + 10; + } + } + + if (constrained_y != 0) { + if (geometry->y > screen_height - 10) { + constrained_y = screen_height - 10; + } else if (constrained_y + geometry->height + + hikari_configuration.border * 2 < + 10) { + constrained_y = -geometry->height - 2 * hikari_configuration.border + 10; + } + } + + if (view->move != NULL) { + view->move(view, constrained_x, constrained_y); + } + + hikari_view_damage_whole(view); + hikari_indicator_damage(&hikari_server.indicator, view); + + geometry->x = constrained_x; + geometry->y = constrained_y; + + refresh_border_geometry(view); + + hikari_view_damage_whole(view); + hikari_indicator_damage(&hikari_server.indicator, view); +} + +void +hikari_view_init(struct hikari_view *view, + enum hikari_view_type type, + struct hikari_workspace *workspace) +{ +#if !defined(NDEBUG) + printf("VIEW INIT %p\n", view); +#endif + view->type = type; + view->flags = hikari_view_hidden_flag; + view->border.state = HIKARI_BORDER_INACTIVE; + view->sheet = NULL; + view->mark = NULL; + view->surface = NULL; + view->maximized_state = NULL; + view->sheet = NULL; + view->output = NULL; + view->group = NULL; + view->title = NULL; + view->tile = NULL; + view->use_csd = false; + view->current_geometry = &view->geometry; + view->current_unmaximized_geometry = &view->geometry; + + hikari_view_unset_dirty(view); + view->pending_operation.tile = NULL; + + wl_list_init(&view->children); +} + +void +hikari_view_fini(struct hikari_view *view) +{ + assert(hikari_view_is_hidden(view)); + +#if !defined(NDEBUG) + printf("DESTROY VIEW %p\n", view); +#endif + + hikari_free(view->title); + + if (view->group != NULL) { + if (hikari_group_is_sheet(view->group)) { + wl_list_remove(&view->group_views); + } else { + detach_from_group(view); + } + } + + if (view->sheet != NULL) { + wl_list_remove(&view->sheet_views); + wl_list_remove(&view->output_views); + } + + if (view->maximized_state != NULL) { + hikari_maximized_state_destroy(view->maximized_state); + } + + if (view->mark != NULL) { + hikari_mark_clear(view->mark); + } + + if (view->tile != NULL) { + struct hikari_tile *tile = view->tile; + + view->tile = NULL; + + hikari_tile_fini(tile); + hikari_free(tile); + } + + if (view->pending_operation.tile != NULL) { + hikari_tile_fini(view->pending_operation.tile); + hikari_free(view->pending_operation.tile); + } +} + +void +hikari_view_set_title(struct hikari_view *view, const char *title) +{ + hikari_free(view->title); + + if (title != NULL) { + view->title = hikari_malloc(strlen(title) + 1); + strcpy(view->title, title); + + if (hikari_server.workspace->focus_view == view) { + assert(!hikari_view_is_hidden(view)); + struct wlr_box *geometry = hikari_view_geometry(view); + struct hikari_output *output = view->output; + hikari_indicator_update_title(&hikari_server.indicator, + geometry, + output, + title, + hikari_configuration.indicator_selected); + } + } else { + view->title = NULL; + } +} + +struct damage_data_t { + struct wlr_box *geometry; + struct hikari_output *output; + struct wlr_surface *surface; + struct hikari_view *view; + + bool whole; +}; + +static void +damage_whole_surface(struct wlr_surface *surface, int sx, int sy, void *data) +{ + struct damage_data_t *damage_data = data; + struct hikari_view *view = damage_data->view; + struct hikari_output *output = damage_data->output; + + if (view->surface == surface) { + hikari_output_add_damage(output, hikari_view_border_geometry(view)); + } else { + struct wlr_box geometry = *damage_data->geometry; + + geometry.x += sx; + geometry.y += sy; + + geometry.width = surface->current.width; + geometry.height = surface->current.height; + + hikari_output_add_damage(output, &geometry); + } +} + +void +hikari_view_damage_whole(struct hikari_view *view) +{ + assert(view != NULL); + + // TODO I know, this needs to be done A LOT better + if (view->use_csd) { + hikari_output_damage_whole(view->output); + return; + } + + struct wlr_box geometry = *hikari_view_geometry(view); + struct hikari_output *output = view->output; + struct damage_data_t damage_data; + + damage_data.geometry = &geometry; + damage_data.output = output; + damage_data.view = view; + + hikari_view_interface_for_each_surface( + (struct hikari_view_interface *)view, damage_whole_surface, &damage_data); +} + +static struct wlr_box * +refresh_unmaximized_geometry(struct hikari_view *view) +{ + assert(view != NULL); + + if (view->tile != NULL) { + return &view->tile->view_geometry; + } else { + return &view->geometry; + } +} + +static struct wlr_box * +refresh_geometry(struct hikari_view *view) +{ + assert(view != NULL); + + if (view->maximized_state != NULL) { + return &view->maximized_state->geometry; + } else { + return refresh_unmaximized_geometry(view); + } +} + +static void +queue_resize(struct hikari_view *view, struct hikari_operation *op) +{ + uint32_t serial = view->resize(view, op->geometry.width, op->geometry.height); + + op->type = HIKARI_OPERATION_TYPE_RESIZE; + op->serial = serial; + + hikari_view_set_dirty(view); +} + +static void +resize(struct hikari_view *view, + int requested_width, + int requested_height, + bool center) +{ + assert(view->maximized_state == NULL); + + struct wlr_box *geometry = hikari_view_geometry(view); + struct hikari_operation *op = &view->pending_operation; + + int min_width; + int min_height; + int max_width; + int max_height; + view->constraints(view, &min_width, &min_height, &max_width, &max_height); + + int new_width; + int new_height; + if (requested_width > max_width) { + new_width = max_width; + } else if (requested_width < min_width) { + new_width = min_width; + } else { + new_width = requested_width; + } + + if (requested_height > max_height) { + new_height = max_height; + } else if (requested_height < min_height) { + new_height = min_height; + } else { + new_height = requested_height; + } + + if (new_height == geometry->height && new_width == geometry->width) { + return; + } + + op->geometry.x = geometry->x; + op->geometry.y = geometry->y; + op->geometry.width = new_width; + op->geometry.height = new_height; + op->center = center; + + queue_resize(view, op); +} + +void +hikari_view_resize_absolute(struct hikari_view *view, int width, int height) +{ + assert(view != NULL); + assert(view->resize != NULL); + assert(view->constraints != NULL); + + if (view->maximized_state != NULL || hikari_view_is_dirty(view)) { + return; + } + + resize(view, width, height, false); +} + +void +hikari_view_resize(struct hikari_view *view, int width, int height) +{ + assert(view != NULL); + assert(view->resize != NULL); + assert(view->constraints != NULL); + + if (view->maximized_state != NULL || hikari_view_is_dirty(view)) { + return; + } + + struct wlr_box *geometry = hikari_view_geometry(view); + + int requested_width = geometry->width + width; + int requested_height = geometry->height + height; + + resize(view, requested_width, requested_height, true); +} + +void +hikari_view_move(struct hikari_view *view, int x, int y) +{ + assert(view != NULL); + + struct wlr_box *geometry = hikari_view_geometry(view); + + move_view(view, geometry, geometry->x + x, geometry->y + y); +} + +void +hikari_view_move_absolute(struct hikari_view *view, int x, int y) +{ + assert(view != NULL); + + struct wlr_box *geometry = hikari_view_geometry(view); + + move_view(view, geometry, x, y); +} + +void +hikari_view_manage(struct hikari_view *view, + struct hikari_sheet *sheet, + struct hikari_group *group) +{ +#if !defined(NDEBUG) + printf("VIEW MANAGE %p\n", view); +#endif + + assert(view != NULL); + assert(sheet != NULL); + assert(group != NULL); + + view->sheet = sheet; + view->output = sheet->workspace->output; + view->group = group; + + wl_list_insert(&sheet->views, &view->sheet_views); + wl_list_insert(&group->views, &view->group_views); + wl_list_insert(&view->output->views, &view->output_views); +} + +void +hikari_view_show(struct hikari_view *view) +{ + assert(view != NULL); + assert(hikari_view_is_hidden(view)); +#if !defined(NDEBUG) + printf("SHOW %p\n", view); +#endif + + wl_list_insert(&view->sheet->workspace->views, &view->workspace_views); + + view->show(view); + + hikari_view_unset_hidden(view); + increase_group_visiblity(view); + hikari_view_damage_whole(view); +} + +void +hikari_view_hide(struct hikari_view *view) +{ + hide(view); + hikari_view_damage_whole(view); +} + +static void +raise_view(struct hikari_view *view) +{ + assert(view != NULL); + + if (view == hikari_workspace_first_view(view->sheet->workspace)) { + return; + } + + move_to_top(view); + place_visibly_above(view); +} + +void +hikari_view_raise(struct hikari_view *view) +{ + assert(view != NULL); + assert(!hikari_view_is_hidden(view)); + + raise_view(view); + hikari_view_damage_whole(view); +} + +void +hikari_view_raise_hidden(struct hikari_view *view) +{ + assert(view != NULL); + assert(hikari_view_is_hidden(view)); + + move_to_top(view); +} + +void +hikari_view_lower(struct hikari_view *view) +{ + assert(view != NULL); + + if (view == hikari_workspace_last_view(view->sheet->workspace)) { + return; + } + + wl_list_remove(&(view->sheet_views)); + wl_list_insert(view->sheet->views.prev, &(view->sheet_views)); + + wl_list_remove(&view->group_views); + wl_list_insert(view->group->views.prev, &view->group_views); + + wl_list_remove(&view->output_views); + wl_list_insert(view->output->views.prev, &view->output_views); + + wl_list_remove(&view->visible_group_views); + wl_list_insert(view->group->visible_views.prev, &view->visible_group_views); + + wl_list_remove(&view->group->visible_server_groups); + wl_list_insert( + hikari_server.visible_groups.prev, &view->group->visible_server_groups); + + wl_list_remove(&view->workspace_views); + wl_list_insert(view->sheet->workspace->views.prev, &view->workspace_views); + + hikari_view_damage_whole(view); +} + +static void +tile_view(struct hikari_view *view, + struct hikari_layout *layout, + struct hikari_tile *tile) +{ + assert(!hikari_view_is_dirty(view)); + + struct hikari_operation *op = &view->pending_operation; + + struct wlr_box *current_geometry = hikari_view_geometry(view); + + op->type = HIKARI_OPERATION_TYPE_TILE; + op->tile = tile; + op->geometry = tile->view_geometry; + + hikari_view_set_dirty(view); + + if (current_geometry->width != op->geometry.width || + current_geometry->height != op->geometry.height) { + op->serial = view->resize(view, op->geometry.width, op->geometry.height); + } else { + hikari_view_commit_pending_operation(view); + } +} + +void +hikari_view_tile(struct hikari_view *view, struct wlr_box *geometry) +{ + assert(!hikari_view_is_dirty(view)); + + struct hikari_layout *layout = view->sheet->workspace->sheet->layout; + + struct hikari_tile *tile = hikari_malloc(sizeof(struct hikari_tile)); + hikari_tile_init(tile, view, geometry, geometry); + + tile_view(view, layout, tile); + + wl_list_insert(layout->tiles.prev, &tile->layout_tiles); +} + +static void +queue_full_maximize(struct hikari_view *view) +{ + assert(view != NULL); + assert(!hikari_view_is_hidden(view)); + + struct hikari_operation *op = &view->pending_operation; + + int width, height; + wlr_output_effective_resolution(view->output->output, &width, &height); + + uint32_t serial = view->resize(view, width, height); + + op->type = HIKARI_OPERATION_TYPE_FULL_MAXIMIZE; + op->serial = serial; + op->geometry.x = 0; + op->geometry.y = 0; + op->geometry.width = width; + op->geometry.height = height; +} + +static void +queue_unmaximize(struct hikari_view *view) +{ + assert(view != NULL); + assert(!hikari_view_is_hidden(view)); + + struct hikari_operation *op = &view->pending_operation; + + op->type = HIKARI_OPERATION_TYPE_UNMAXIMIZE; + if (view->tile != NULL) { + op->geometry = view->tile->view_geometry; + } else { + op->geometry = view->geometry; + } + + uint32_t serial = view->resize(view, op->geometry.width, op->geometry.height); + + op->serial = serial; +} + +void +hikari_view_toggle_full_maximize(struct hikari_view *view) +{ + assert(view != NULL); + assert(!hikari_view_is_hidden(view)); + + if (hikari_view_is_dirty(view)) { + return; + } + + if (hikari_view_is_fully_maximized(view)) { + queue_unmaximize(view); + } else { + queue_full_maximize(view); + } + + hikari_view_set_dirty(view); +} + +static void +queue_horizontal_maximize(struct hikari_view *view) +{ + assert(view != NULL); + assert(!hikari_view_is_hidden(view)); + + struct hikari_operation *op = &view->pending_operation; + + int width, height; + wlr_output_effective_resolution(view->output->output, &width, &height); + + struct wlr_box *geometry = view->current_unmaximized_geometry; + + uint32_t serial = view->resize(view, width, geometry->height); + + op->type = HIKARI_OPERATION_TYPE_HORIZONTAL_MAXIMIZE; + op->serial = serial; + op->geometry.x = 0; + op->geometry.y = geometry->y; + op->geometry.height = geometry->height; + op->geometry.width = width; +} + +static void +queue_vertical_maximize(struct hikari_view *view) +{ + assert(view != NULL); + assert(!hikari_view_is_hidden(view)); + + struct hikari_operation *op = &view->pending_operation; + + int width, height; + wlr_output_effective_resolution(view->output->output, &width, &height); + + struct wlr_box *geometry = view->current_unmaximized_geometry; + + uint32_t serial = view->resize(view, geometry->width, height); + + op->type = HIKARI_OPERATION_TYPE_VERTICAL_MAXIMIZE; + op->serial = serial; + op->geometry.x = geometry->x; + op->geometry.y = 0; + op->geometry.height = height; + op->geometry.width = geometry->width; +} + +void +hikari_view_toggle_vertical_maximize(struct hikari_view *view) +{ + assert(view != NULL); + assert(!hikari_view_is_hidden(view)); + + if (hikari_view_is_dirty(view)) { + return; + } + + if (view->maximized_state != NULL) { + switch (view->maximized_state->maximization) { + case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED: + queue_horizontal_maximize(view); + break; + + case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED: + queue_unmaximize(view); + break; + + case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED: + queue_full_maximize(view); + break; + } + } else { + queue_vertical_maximize(view); + } + + hikari_view_set_dirty(view); +} + +void +hikari_view_toggle_horizontal_maximize(struct hikari_view *view) +{ + assert(view != NULL); + assert(!hikari_view_is_hidden(view)); + + if (view->maximized_state != NULL) { + switch (view->maximized_state->maximization) { + case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED: + queue_vertical_maximize(view); + break; + + case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED: + queue_full_maximize(view); + break; + + case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED: + queue_unmaximize(view); + break; + } + } else { + queue_horizontal_maximize(view); + } + + hikari_view_set_dirty(view); +} + +void +hikari_view_toggle_floating(struct hikari_view *view) +{ + if (!hikari_view_is_floating(view)) { + if (hikari_view_is_tiled(view)) { + hikari_view_reset_geometry(view); + } + hikari_view_set_floating(view); + } else { + hikari_view_unset_floating(view); + } +} + +void +hikari_view_reset_geometry(struct hikari_view *view) +{ + uint32_t serial = + view->resize(view, view->geometry.width, view->geometry.height); + + struct hikari_operation *op = &view->pending_operation; + + op->type = HIKARI_OPERATION_TYPE_RESET; + op->serial = serial; + op->geometry = view->geometry; + + hikari_view_set_dirty(view); +} + +void +hikari_view_pin_to_sheet(struct hikari_view *view, struct hikari_sheet *sheet) +{ + assert(view != NULL); + assert(sheet != NULL); + assert(!hikari_view_is_hidden(view)); + + if (view->sheet == sheet) { + if (view->sheet->workspace->sheet != sheet && sheet->nr != 0) { + hide(view); + view->sheet = sheet; + move_to_top(view); + } else { + raise_view(view); + } + } else { + if (view->sheet->group == view->group) { + regroup(view, sheet->group); + } else { + if (view->sheet->workspace->sheet != sheet && sheet->nr != 0) { + hide(view); + } else { + place_visibly_above(view); + } + + view->sheet = sheet; + move_to_top(view); + } + } + + hikari_view_damage_whole(view); +} + +void +hikari_view_center_cursor(struct hikari_view *view) +{ + assert(view != NULL); + + struct wlr_box *geometry = hikari_view_geometry(view); + struct hikari_output *output = view->output; + + int x = output->geometry.x + geometry->x + geometry->width / 2; + int y = output->geometry.y + geometry->y + geometry->height / 2; + + wlr_cursor_warp(hikari_server.cursor, NULL, x, y); +} + +void +hikari_view_top_left_cursor(struct hikari_view *view) +{ + assert(view != NULL); + + struct wlr_box *geometry = hikari_view_geometry(view); + struct hikari_output *output = view->output; + + int x = output->geometry.x + geometry->x; + int y = output->geometry.y + geometry->y; + + wlr_cursor_warp(hikari_server.cursor, NULL, x, y); +} + +void +hikari_view_bottom_right_cursor(struct hikari_view *view) +{ + assert(view != NULL); + + struct wlr_box *geometry = hikari_view_geometry(view); + struct hikari_output *output = view->output; + + int x = output->geometry.x + geometry->x + geometry->width; + int y = output->geometry.y + geometry->y + geometry->height; + + wlr_cursor_warp(hikari_server.cursor, NULL, x, y); +} + +void +hikari_view_toggle_iconified(struct hikari_view *view) +{ + if (hikari_view_is_iconified(view)) { + hikari_view_unset_iconified(view); + } else { + hikari_view_set_iconified(view); + } +} + +void +hikari_view_group(struct hikari_view *view, struct hikari_group *group) +{ + assert(view != NULL); + assert(group != NULL); + assert(view->group != NULL); + + if (view->group == group) { + return; + } + + regroup(view, group); + hikari_view_damage_whole(view); +} + +void +hikari_view_exchange(struct hikari_view *from, struct hikari_view *to) +{ + assert(from != NULL); + assert(from->tile != NULL); + assert(to != NULL); + assert(to->tile != NULL); + assert(to->tile->view->sheet == from->tile->view->sheet); + assert(!hikari_view_is_dirty(from)); + assert(!hikari_view_is_dirty(to)); + + struct hikari_layout *layout = from->sheet->workspace->sheet->layout; + + struct wlr_box *from_geometry = hikari_view_geometry(from); + struct wlr_box *to_geometry = hikari_view_geometry(to); + + struct hikari_tile *from_tile = hikari_malloc(sizeof(struct hikari_tile)); + struct hikari_tile *to_tile = hikari_malloc(sizeof(struct hikari_tile)); + + hikari_tile_init(from_tile, from, to_geometry, to_geometry); + hikari_tile_init(to_tile, to, from_geometry, from_geometry); + + wl_list_insert(&from->tile->layout_tiles, &to_tile->layout_tiles); + wl_list_insert(&to->tile->layout_tiles, &from_tile->layout_tiles); + + tile_view(from, layout, from_tile); + tile_view(to, layout, to_tile); +} + +static void +destroy_subsurface_handler(struct wl_listener *listener, void *data) +{ + struct hikari_view_subsurface *view_subsurface = + wl_container_of(listener, view_subsurface, destroy); + + hikari_view_subsurface_fini(view_subsurface); + + hikari_free(view_subsurface); +} + +void +hikari_view_subsurface_init(struct hikari_view_subsurface *view_subsurface, + struct hikari_view *parent, + struct wlr_subsurface *subsurface) +{ + view_subsurface->subsurface = subsurface; + + view_subsurface->destroy.notify = destroy_subsurface_handler; + wl_signal_add( + &subsurface->surface->events.destroy, &view_subsurface->destroy); + + hikari_view_child_init( + (struct hikari_view_child *)view_subsurface, parent, subsurface->surface); +} + +void +hikari_view_child_fini(struct hikari_view_child *view_child) +{ + wl_list_remove(&view_child->commit.link); + wl_list_remove(&view_child->new_subsurface.link); +} + +void +hikari_view_subsurface_fini(struct hikari_view_subsurface *view_subsurface) +{ + hikari_view_child_fini(&view_subsurface->view_child); + wl_list_remove(&view_subsurface->destroy.link); +} + +static void +new_subsurface_handler(struct wl_listener *listener, void *data) +{ + struct hikari_view_child *view_child = + wl_container_of(listener, view_child, new_subsurface); + + struct wlr_subsurface *wlr_subsurface = data; + + struct hikari_view_subsurface *view_subsurface = + hikari_malloc(sizeof(struct hikari_view_subsurface)); + + hikari_view_subsurface_init( + view_subsurface, view_child->parent, wlr_subsurface); +} + +static void +damage_surface(struct wlr_surface *surface, int sx, int sy, void *data) +{ + struct damage_data_t *damage_data = data; + + if (damage_data->whole) { + damage_whole_surface(surface, sx, sy, data); + } else { + struct wlr_box *geometry = damage_data->geometry; + struct hikari_output *output = damage_data->output; + + pixman_region32_t damage; + pixman_region32_init(&damage); + + wlr_surface_get_effective_damage(surface, &damage); + if (pixman_region32_not_empty(&damage)) { + pixman_region32_translate(&damage, geometry->x + sx, geometry->y + sy); + wlr_output_damage_add(output->damage, &damage); + } else { + damage_whole_surface(surface, sx, sy, data); + } + + pixman_region32_fini(&damage); + } +} + +static void +damage_single_surface(struct wlr_surface *surface, int sx, int sy, void *data) +{ + struct damage_data_t *damage_data = data; + + if (damage_data->surface == surface) { + damage_surface(surface, sx, sy, data); + } +} + +static void +commit_view_child_handler(struct wl_listener *listener, void *data) +{ + struct hikari_view_child *view_child = + wl_container_of(listener, view_child, commit); + + hikari_view_damage_surface(view_child->parent, view_child->surface, false); +} + +void +hikari_view_child_init(struct hikari_view_child *view_child, + struct hikari_view *parent, + struct wlr_surface *surface) +{ + view_child->parent = parent; + view_child->surface = surface; + + view_child->new_subsurface.notify = new_subsurface_handler; + wl_signal_add(&surface->events.new_subsurface, &view_child->new_subsurface); + + view_child->commit.notify = commit_view_child_handler; + wl_signal_add(&surface->events.commit, &view_child->commit); +} + +void +hikari_view_damage_surface( + struct hikari_view *view, struct wlr_surface *surface, bool whole) +{ + assert(view != NULL); + + // TODO I know, this needs to be done A LOT better + if (view->use_csd) { + hikari_output_damage_whole(view->output); + return; + } + + struct damage_data_t damage_data; + + damage_data.geometry = hikari_view_geometry(view); + damage_data.output = view->output; + damage_data.surface = surface; + damage_data.whole = whole; + damage_data.view = view; + + hikari_view_interface_for_each_surface((struct hikari_view_interface *)view, + damage_single_surface, + &damage_data); +} + +void +hikari_view_refresh_geometry(struct hikari_view *view, struct wlr_box *geometry) +{ + struct wlr_box *new_geometry = refresh_geometry(view); + + memcpy(new_geometry, geometry, sizeof(struct wlr_box)); + + view->current_geometry = new_geometry; + view->current_unmaximized_geometry = refresh_unmaximized_geometry(view); + + refresh_border_geometry(view); +} + +static void +commit_pending_geometry(struct hikari_view *view, + struct wlr_box *pending_geometry, + struct wlr_box *geometry_before) +{ + struct hikari_output *output = view->output; + hikari_view_refresh_geometry(view, pending_geometry); + + hikari_output_add_damage(output, geometry_before); + hikari_view_damage_whole(view); +} + +static void +commit_resize(struct hikari_view *view, + struct hikari_operation *operation, + struct wlr_box *geometry_before) +{ + raise_view(view); + commit_pending_geometry(view, &operation->geometry, geometry_before); + + if (operation->center) { + hikari_view_center_cursor(view); + } +} + +static void +commit_reset(struct hikari_view *view, + struct hikari_operation *operation, + struct wlr_box *geometry_before) +{ + if (view->tile) { + struct hikari_tile *tile = view->tile; + struct hikari_sheet *sheet = view->sheet; + struct hikari_layout *layout = sheet->layout; + + assert(layout != NULL); + + view->tile = NULL; + + hikari_tile_fini(tile); + hikari_free(tile); + + if (wl_list_empty(&layout->tiles)) { + hikari_layout_fini(layout); + hikari_free(layout); + sheet->layout = NULL; + } + } + + if (view->maximized_state) { + hikari_maximized_state_destroy(view->maximized_state); + view->maximized_state = NULL; + if (!view->use_csd) { + view->border.state = HIKARI_BORDER_INACTIVE; + } + } + + raise_view(view); + commit_pending_geometry(view, &operation->geometry, geometry_before); + + hikari_view_center_cursor(view); +} + +static void +commit_unmaximize(struct hikari_view *view, + struct hikari_operation *operation, + struct wlr_box *geometry_before) +{ + hikari_free(view->maximized_state); + view->maximized_state = NULL; + + if (!view->use_csd) { + view->border.state = HIKARI_BORDER_ACTIVE; + } + + raise_view(view); + commit_pending_geometry(view, &operation->geometry, geometry_before); + + hikari_view_center_cursor(view); +} + +static void +commit_full_maximize(struct hikari_view *view, + struct hikari_operation *operation, + struct wlr_box *geometry_before) +{ + if (!view->maximized_state) { + view->maximized_state = + hikari_malloc(sizeof(struct hikari_maximized_state)); + } + + view->maximized_state->maximization = HIKARI_MAXIMIZATION_FULLY_MAXIMIZED; + view->maximized_state->geometry = operation->geometry; + + if (!view->use_csd) { + view->border.state = HIKARI_BORDER_NONE; + } + + raise_view(view); + commit_pending_geometry(view, &operation->geometry, geometry_before); + + hikari_view_center_cursor(view); +} + +static void +commit_vertical_maximize(struct hikari_view *view, + struct hikari_operation *operation, + struct wlr_box *geometry_before) +{ + if (!view->maximized_state) { + view->maximized_state = + hikari_malloc(sizeof(struct hikari_maximized_state)); + } else { + switch (view->maximized_state->maximization) { + case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED: + commit_full_maximize(view, operation, geometry_before); + return; + + case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED: + if (!view->use_csd) { + view->border.state = HIKARI_BORDER_INACTIVE; + } + break; + + case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED: + assert(false); + break; + } + } + + view->maximized_state->maximization = + HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED; + view->maximized_state->geometry = operation->geometry; + + raise_view(view); + commit_pending_geometry(view, &operation->geometry, geometry_before); + + hikari_view_center_cursor(view); +} + +static void +commit_horizontal_maximize(struct hikari_view *view, + struct hikari_operation *operation, + struct wlr_box *geometry_before) +{ + if (!view->maximized_state) { + view->maximized_state = + hikari_malloc(sizeof(struct hikari_maximized_state)); + } else { + switch (view->maximized_state->maximization) { + case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED: + commit_full_maximize(view, operation, geometry_before); + return; + + case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED: + if (!view->use_csd) { + view->border.state = HIKARI_BORDER_INACTIVE; + } + break; + + case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED: + assert(false); + break; + } + } + + view->maximized_state->maximization = + HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED; + view->maximized_state->geometry = operation->geometry; + + raise_view(view); + commit_pending_geometry(view, &operation->geometry, geometry_before); + + hikari_view_center_cursor(view); +} + +static void +commit_tile(struct hikari_view *view, + struct hikari_operation *operation, + struct wlr_box *geometry_before) +{ + if (view->maximized_state) { + hikari_maximized_state_destroy(view->maximized_state); + view->maximized_state = NULL; + if (!view->use_csd) { + view->border.state = HIKARI_BORDER_INACTIVE; + } + } + + if (view->tile != NULL) { + struct hikari_tile *tile = view->tile; + + view->tile = NULL; + + hikari_tile_fini(tile); + hikari_free(tile); + } + + view->tile = operation->tile; + operation->tile = NULL; + + commit_pending_geometry(view, &operation->geometry, geometry_before); +} + +static void +commit_operation(struct hikari_operation *operation, struct hikari_view *view) +{ + struct wlr_box geometry_before = *hikari_view_border_geometry(view); + + switch (operation->type) { + case HIKARI_OPERATION_TYPE_RESIZE: + commit_resize(view, operation, &geometry_before); + break; + + case HIKARI_OPERATION_TYPE_RESET: + commit_reset(view, operation, &geometry_before); + break; + + case HIKARI_OPERATION_TYPE_UNMAXIMIZE: + commit_unmaximize(view, operation, &geometry_before); + break; + + case HIKARI_OPERATION_TYPE_FULL_MAXIMIZE: + commit_full_maximize(view, operation, &geometry_before); + break; + + case HIKARI_OPERATION_TYPE_VERTICAL_MAXIMIZE: + commit_vertical_maximize(view, operation, &geometry_before); + break; + + case HIKARI_OPERATION_TYPE_HORIZONTAL_MAXIMIZE: + commit_horizontal_maximize(view, operation, &geometry_before); + break; + + case HIKARI_OPERATION_TYPE_TILE: + commit_tile(view, operation, &geometry_before); + break; + } +} + +void +hikari_view_commit_pending_operation(struct hikari_view *view) +{ + assert(view != NULL); + assert(view->pending_operation.dirty); + + commit_operation(&view->pending_operation, view); + hikari_view_unset_dirty(view); +} + +void +hikari_view_activate(struct hikari_view *view, bool active) +{ + assert(view != NULL); + + if (view->activate) { + if (view->border.state != HIKARI_BORDER_NONE) { + view->border.state = + active ? HIKARI_BORDER_ACTIVE : HIKARI_BORDER_INACTIVE; + } + view->activate(view, active); + } +} diff --git a/src/view_autoconf.c b/src/view_autoconf.c new file mode 100644 index 0000000..4b370cd --- /dev/null +++ b/src/view_autoconf.c @@ -0,0 +1,15 @@ +#include + +#include +#include + +#include + +void +hikari_view_autoconf_fini(struct hikari_view_autoconf *autoconf) +{ + assert(autoconf != NULL); + + hikari_free(autoconf->app_id); + hikari_free(autoconf->group_name); +} diff --git a/src/workspace.c b/src/workspace.c new file mode 100644 index 0000000..cf06471 --- /dev/null +++ b/src/workspace.c @@ -0,0 +1,1064 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FOCUS_GUARD(workspace, view) \ + struct hikari_view *view = workspace->focus_view; \ + \ + if (focus_view == NULL) { \ + return; \ + } + +static void +render_texture(struct wlr_output *output, + pixman_region32_t *damage, + struct wlr_texture *texture, + struct wlr_renderer *renderer, + const float matrix[static 9], + struct wlr_box *box) +{ + pixman_region32_t local_damage; + pixman_region32_init(&local_damage); + pixman_region32_union_rect( + &local_damage, &local_damage, box->x, box->y, box->width, box->height); + + pixman_region32_intersect(&local_damage, &local_damage, damage); + + bool damaged = pixman_region32_not_empty(&local_damage); + if (!damaged) { + goto damage_finish; + } + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&local_damage, &nrects); + for (int i = 0; i < nrects; ++i) { + hikari_output_scissor_render(output, renderer, &rects[i]); + wlr_render_texture_with_matrix(renderer, texture, matrix, 1); + } + +damage_finish: + pixman_region32_fini(&local_damage); +} + +void +hikari_workspace_init( + struct hikari_workspace *workspace, struct hikari_output *output) +{ + wl_list_init(&workspace->views); + wl_list_init(&hikari_server.visible_groups); + workspace->background = NULL; + workspace->output = output; + workspace->focus_view = NULL; + workspace->sheets = calloc(HIKARI_NR_OF_SHEETS, sizeof(struct hikari_sheet)); + + for (int i = 0; i < HIKARI_NR_OF_SHEETS; i++) { + hikari_sheet_init(&workspace->sheets[i], i, workspace); + } + + workspace->alternate_sheet = &workspace->sheets[0]; + workspace->sheet = &workspace->sheets[1]; + + wl_list_insert(hikari_server.workspaces.prev, &workspace->server_workspaces); +} + +void +hikari_workspace_fini(struct hikari_workspace *workspace) +{ + for (int i = 0; i < HIKARI_NR_OF_SHEETS; i++) { + hikari_sheet_fini(&workspace->sheets[i]); + } +} + +void +hikari_workspace_render_background( + struct hikari_workspace *workspace, struct hikari_render_data *render_data) +{ + if (workspace->background == NULL) { + return; + } + + float matrix[9]; + struct wlr_output *output = workspace->output->output; // render_data->output; + + struct wlr_box geometry = { .x = 0, .y = 0 }; + wlr_output_transformed_resolution(output, &geometry.width, &geometry.height); + + wlr_matrix_project_box(matrix, &geometry, 0, 0, output->transform_matrix); + + render_texture(output, + render_data->damage, + workspace->background, + render_data->renderer, + matrix, + &geometry); +} + +/* static int64_t timespec_to_msec(const struct timespec *a) { */ +/* return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; */ +/* } */ + +void +hikari_workspace_quit_view(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_view_quit(focus_view); +} + +void +hikari_workspace_clear(struct hikari_workspace *workspace) +{ + struct hikari_view *view = NULL, *view_tmp = NULL; + + wl_list_for_each_reverse_safe ( + view, view_tmp, &(workspace->views), workspace_views) { + hikari_view_hide(view); + } + + hikari_server_cursor_focus(); +} + +static void +display_sheet(struct hikari_workspace *workspace, struct hikari_sheet *sheet) +{ + struct hikari_view *view = NULL; + + hikari_workspace_clear(workspace); + + if (sheet != workspace->sheet) { + workspace->alternate_sheet = workspace->sheet; + workspace->sheet = sheet; + } + + struct hikari_sheet *sheets = workspace->sheets; + + wl_list_for_each_reverse (view, &sheets[0].views, sheet_views) { + if (!hikari_view_is_iconified(view)) { + hikari_view_show(view); + } + } + + if (sheet->nr != 0) { + view = NULL; + wl_list_for_each_reverse (view, &sheet->views, sheet_views) { + if (!hikari_view_is_iconified(view)) { + hikari_view_show(view); + } + } + } + + hikari_server_cursor_focus(); +} + +#define DISPLAY_SHEET(name, sheet) \ + void hikari_workspace_display_sheet_##name( \ + struct hikari_workspace *workspace) \ + { \ + display_sheet(workspace, sheet); \ + } + +DISPLAY_SHEET(0, &workspace->sheets[0]) +DISPLAY_SHEET(1, &workspace->sheets[1]) +DISPLAY_SHEET(2, &workspace->sheets[2]) +DISPLAY_SHEET(3, &workspace->sheets[3]) +DISPLAY_SHEET(4, &workspace->sheets[4]) +DISPLAY_SHEET(5, &workspace->sheets[5]) +DISPLAY_SHEET(6, &workspace->sheets[6]) +DISPLAY_SHEET(7, &workspace->sheets[7]) +DISPLAY_SHEET(8, &workspace->sheets[8]) +DISPLAY_SHEET(9, &workspace->sheets[9]) +DISPLAY_SHEET(alternate, workspace->alternate_sheet) +DISPLAY_SHEET(current, workspace->sheet) +DISPLAY_SHEET(next, hikari_sheet_next(workspace->sheet)) +DISPLAY_SHEET(prev, hikari_sheet_prev(workspace->sheet)) +#undef DISPLAY_SHEET + +void +hikari_workspace_switch_sheet( + struct hikari_workspace *workspace, struct hikari_sheet *sheet) +{ + assert(workspace == sheet->workspace); + + display_sheet(workspace, sheet); +} + +void +hikari_workspace_switch_to_next_sheet(struct hikari_workspace *workspace) +{ + if (workspace->sheet->nr == 0) { + return; + } + + uint8_t nr = workspace->sheet->nr == 9 ? 1 : workspace->sheet->nr + 1; + + display_sheet(workspace, &workspace->sheets[nr]); +} + +void +hikari_workspace_switch_to_prev_sheet(struct hikari_workspace *workspace) +{ + if (workspace->sheet->nr == 0) { + return; + } + + uint8_t nr = workspace->sheet->nr == 1 ? 9 : workspace->sheet->nr - 1; + + display_sheet(workspace, &workspace->sheets[nr]); +} + +void +hikari_workspace_switch_to_next_inhabited_sheet( + struct hikari_workspace *workspace) +{ + struct hikari_sheet *sheet = hikari_sheet_next_inhabited(workspace->sheet); + + if (sheet == workspace->sheet) { + return; + } + + display_sheet(workspace, sheet); +} + +void +hikari_workspace_switch_to_prev_inhabited_sheet( + struct hikari_workspace *workspace) +{ + struct hikari_sheet *sheet = hikari_sheet_prev_inhabited(workspace->sheet); + + if (sheet == workspace->sheet) { + return; + } + + display_sheet(workspace, sheet); +} + +struct hikari_view * +hikari_workspace_first_view(struct hikari_workspace *workspace) +{ + struct hikari_view *view; + + if (wl_list_empty(&workspace->views)) { + return NULL; + } + + view = wl_container_of(workspace->views.next, view, workspace_views); + + return view; +} + +struct hikari_view * +hikari_workspace_last_view(struct hikari_workspace *workspace) +{ + struct hikari_view *view; + + if (wl_list_empty(&workspace->views)) { + return NULL; + } + + view = wl_container_of(workspace->views.prev, view, workspace_views); + + return view; +} + +#define CYCLE_VIEW(link) \ + struct hikari_view *hikari_workspace_##link##_view( \ + struct hikari_workspace *workspace) \ + { \ + if (wl_list_empty(&workspace->views)) { \ + return NULL; \ + } \ + \ + struct hikari_view *view; \ + struct hikari_view *focus_view = workspace->focus_view; \ + \ + if (focus_view == NULL) { \ + view = wl_container_of(workspace->views.link, view, workspace_views); \ + } else { \ + struct wl_list *link = focus_view->workspace_views.link; \ + \ + if (link != &workspace->views) { \ + view = wl_container_of(link, view, workspace_views); \ + } else { \ + view = wl_container_of(workspace->views.link, view, workspace_views); \ + } \ + } \ + \ + return view; \ + } + +CYCLE_VIEW(next) +CYCLE_VIEW(prev) +#undef CYCLE_VIEW + +#define CYCLE_LAYOUT_VIEW(fallback, link) \ + \ + struct hikari_view *hikari_workspace_##fallback##_layout_view( \ + struct hikari_workspace *workspace) \ + { \ + struct hikari_layout *layout = workspace->sheet->layout; \ + if (workspace->sheet->layout == NULL) { \ + return NULL; \ + } \ + \ + return hikari_layout_##fallback##_view(layout); \ + } \ + \ + struct hikari_view *hikari_workspace_##link##_layout_view( \ + struct hikari_workspace *workspace) \ + { \ + struct hikari_layout *layout = workspace->sheet->layout; \ + if (workspace->sheet->layout == NULL) { \ + return NULL; \ + } \ + \ + struct hikari_view *focus_view = workspace->focus_view; \ + \ + if (focus_view == NULL || focus_view->tile == NULL) { \ + return hikari_layout_##fallback##_view(layout); \ + } else { \ + return hikari_tile_##link##_view(focus_view->tile); \ + } \ + } + +CYCLE_LAYOUT_VIEW(first, next) +CYCLE_LAYOUT_VIEW(last, prev) +#undef CYCLE_LAYOUT_VIEW + +#define CYCLE_GROUP_VIEW(link) \ + struct hikari_view *hikari_workspace_##link##_group_view( \ + struct hikari_workspace *workspace) \ + { \ + struct hikari_view *focus_view = workspace->focus_view; \ + if (focus_view == NULL) { \ + return NULL; \ + } \ + \ + return hikari_group_##link##_view( \ + focus_view->group, hikari_server.workspace); \ + } + +CYCLE_GROUP_VIEW(first) +CYCLE_GROUP_VIEW(last) +#undef CYCLE_GROUP_VIEW + +#define CYCLE_SHEET_VIEW(link, fallback) \ + struct hikari_view *hikari_workspace_##link##_sheet_view( \ + struct hikari_workspace *workspace) \ + { \ + struct hikari_view *focus_view = workspace->focus_view; \ + struct hikari_sheet *sheet = workspace->sheet; \ + \ + if (focus_view != NULL) { \ + if (focus_view->sheet != sheet) { \ + return hikari_sheet_##fallback##_view(sheet); \ + } else { \ + return hikari_sheet_##link##_view(sheet, focus_view); \ + } \ + } else { \ + return hikari_sheet_##fallback##_view(sheet); \ + } \ + } + +CYCLE_SHEET_VIEW(next, first) +CYCLE_SHEET_VIEW(prev, last) +#undef CYCLE_SHEET_VIEW + +#define CYCLE_GROUP_VIEW(link) \ + struct hikari_view *hikari_workspace_##link##_group_view( \ + struct hikari_workspace *workspace) \ + { \ + struct hikari_view *focus_view = workspace->focus_view; \ + if (focus_view == NULL) { \ + return NULL; \ + } \ + \ + struct hikari_view *view; \ + struct hikari_group *group = focus_view->group; \ + struct wl_list *link = focus_view->visible_group_views.link; \ + \ + if (link != &group->visible_views) { \ + view = wl_container_of( \ + focus_view->visible_group_views.link, view, visible_group_views); \ + } else { \ + view = wl_container_of( \ + group->visible_views.link, view, visible_group_views); \ + } \ + \ + return view; \ + } + +CYCLE_GROUP_VIEW(next) +CYCLE_GROUP_VIEW(prev) +#undef CYCLE_GROUP_VIEW + +#define CYCLE_GROUP(link) \ + struct hikari_view *hikari_workspace_##link##_group( \ + struct hikari_workspace *workspace) \ + { \ + if (wl_list_empty(&hikari_server.visible_groups)) { \ + return NULL; \ + } \ + \ + struct hikari_view *focus_view = workspace->focus_view; \ + \ + struct hikari_group *group; \ + if (focus_view == NULL) { \ + group = wl_container_of( \ + hikari_server.visible_groups.link, group, visible_server_groups); \ + } else { \ + struct wl_list *link = focus_view->group->visible_server_groups.link; \ + \ + if (link != &hikari_server.visible_groups) { \ + group = wl_container_of(link, group, visible_server_groups); \ + } else { \ + group = wl_container_of( \ + hikari_server.visible_groups.link, group, visible_server_groups); \ + } \ + \ + if (group == focus_view->group) { \ + return focus_view; \ + } \ + } \ + \ + struct hikari_view *view = \ + wl_container_of(group->visible_views.next, view, visible_group_views); \ + \ + return view; \ + } + +CYCLE_GROUP(next) +CYCLE_GROUP(prev) +#undef CYCLE_GROUP + +void +hikari_workspace_focus_view( + struct hikari_workspace *workspace, struct hikari_view *view) +{ + /* assert(workspace->focus_view != view && hikari_server.workspace != + * workspace); */ + struct wlr_seat *seat = hikari_server.seat; + + if (!hikari_server_in_normal_mode()) { + hikari_server_enter_normal_mode(NULL); + } + + if (hikari_server.workspace->focus_view != NULL) { + hikari_view_activate(hikari_server.workspace->focus_view, false); + wlr_seat_keyboard_end_grab(seat); + } + + if (view != NULL) { + assert(!hikari_view_is_hidden(view)); + + hikari_view_activate(view, true); + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); + + wlr_seat_keyboard_notify_enter(seat, + view->surface, + keyboard->keycodes, + keyboard->num_keycodes, + &keyboard->modifiers); + + hikari_indicator_update(&hikari_server.indicator, + view, + hikari_configuration.indicator_selected); + + hikari_server_refresh_indication(); + } else { + wlr_seat_keyboard_clear_focus(seat); + } + + hikari_server.workspace = workspace; + workspace->focus_view = view; +} + +void +hikari_workspace_raise_view(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_view_raise(focus_view); + + hikari_server_refresh_indication(); +} + +void +hikari_workspace_raise_group(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + struct hikari_group *group = focus_view->group; + struct hikari_view *tail = NULL; + + struct hikari_view *view = NULL, *view_temp = NULL; + wl_list_for_each_reverse_safe ( + view, view_temp, &workspace->views, workspace_views) { + if (view == tail) { + break; + } + + if (view->group == group && view != focus_view) { + if (tail == NULL) { + tail = view; + } + hikari_view_raise(view); + } + } + + hikari_view_raise(focus_view); +} + +void +hikari_workspace_lower_view(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_view_lower(focus_view); + + hikari_server_cursor_focus(); + hikari_server_refresh_indication(); +} + +void +hikari_workspace_lower_group(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + struct hikari_group *group = focus_view->group; + + hikari_view_lower(focus_view); + + struct hikari_view *view = NULL, *view_temp = NULL; + wl_list_for_each_safe (view, view_temp, &workspace->views, workspace_views) { + if (view == focus_view) { + break; + } + + if (view->group == group) { + hikari_view_lower(view); + } + } +} + +void +hikari_workspace_hide_view(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_view_hide(focus_view); + hikari_server_cursor_focus(); +} + +void +hikari_workspace_move_view(struct hikari_workspace *workspace, int dx, int dy) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_server_set_cycling(); + + // TODO this is the only place we need this + hikari_view_move(focus_view, dx, dy); +} + +void +hikari_workspace_only_view(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + struct hikari_view *view = NULL; + struct hikari_view *view_tmp; + wl_list_for_each_safe (view, view_tmp, &workspace->views, workspace_views) { + if (view != focus_view) { + hikari_view_hide(view); + } + } + + hikari_server_cursor_focus(); +} + +void +hikari_workspace_resize_view( + struct hikari_workspace *workspace, int dwidth, int dheight) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_server_set_cycling(); + + hikari_view_resize(focus_view, dwidth, dheight); +} + +void +hikari_workspace_snap_view_up(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_server_set_cycling(); + + struct wlr_box *view_geometry = hikari_view_geometry(focus_view); + + int lookahead = + view_geometry->y - hikari_configuration.border - hikari_configuration.gap; + int view_right = view_geometry->x + view_geometry->width; + int y; + + bool found = false; + struct hikari_view *view = NULL; + struct wlr_box *geometry = NULL; + wl_list_for_each (view, &workspace->views, workspace_views) { + if (view == focus_view) { + continue; + } + + geometry = hikari_view_geometry(view); + + int right = geometry->x + geometry->width; + + if (geometry->y + geometry->height + hikari_configuration.border < + lookahead && + ((view_right >= geometry->x && geometry->x >= view_geometry->x) || + (right >= view_geometry->x && view_geometry->x >= geometry->x))) { + found = true; + y = geometry->y + hikari_configuration.gap + geometry->height + + hikari_configuration.border; + break; + } + } + + if (!found) { + y = hikari_configuration.border; + } + + hikari_view_move_absolute(focus_view, view_geometry->x, y); +} + +void +hikari_workspace_snap_view_down(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_server_set_cycling(); + + struct wlr_box *view_geometry = hikari_view_geometry(focus_view); + struct wlr_output *output = workspace->output->output; + + int lookahead = view_geometry->y + view_geometry->height + + hikari_configuration.border + hikari_configuration.gap; + int view_right = view_geometry->x + view_geometry->width; + int y; + + bool found = false; + struct hikari_view *view = NULL; + struct wlr_box *geometry = NULL; + wl_list_for_each (view, &workspace->views, workspace_views) { + if (view == focus_view) { + continue; + } + + geometry = hikari_view_geometry(view); + + int right = geometry->x + geometry->width; + + if (geometry->y > lookahead && + ((view_right >= geometry->x && geometry->x >= view_geometry->x) || + (right >= view_geometry->x && view_geometry->x >= geometry->x))) { + found = true; + y = geometry->y - hikari_configuration.gap - hikari_configuration.border - + view_geometry->height; + break; + } + } + + if (!found) { + int ow, oh; + + wlr_output_transformed_resolution(output, &ow, &oh); + y = oh - view_geometry->height - hikari_configuration.border; + } + + hikari_view_move_absolute(focus_view, view_geometry->x, y); +} + +void +hikari_workspace_snap_view_left(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_server_set_cycling(); + + struct wlr_box *view_geometry = hikari_view_geometry(focus_view); + + int lookahead = + view_geometry->x - hikari_configuration.gap - hikari_configuration.border; + int view_bottom = view_geometry->y + view_geometry->height; + int x; + + bool found = false; + struct hikari_view *view = NULL; + struct wlr_box *geometry = NULL; + wl_list_for_each (view, &workspace->views, workspace_views) { + if (view == focus_view) { + continue; + } + + geometry = hikari_view_geometry(view); + + int bottom = geometry->y + geometry->height; + + if (geometry->x + geometry->width + hikari_configuration.border < + lookahead && + ((view_bottom >= geometry->y && geometry->y >= view_geometry->y) || + (bottom >= view_geometry->y && view_geometry->y >= geometry->y))) { + found = true; + x = geometry->x + geometry->width + hikari_configuration.gap + + hikari_configuration.border; + break; + } + } + + if (!found) { + x = hikari_configuration.border; + } + + hikari_view_move_absolute(focus_view, x, view_geometry->y); +} + +void +hikari_workspace_snap_view_right(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_server_set_cycling(); + + struct wlr_box *view_geometry = hikari_view_geometry(focus_view); + struct wlr_output *output = workspace->output->output; + + int lookahead = view_geometry->x + view_geometry->width + + hikari_configuration.gap + hikari_configuration.border; + int view_bottom = view_geometry->y + view_geometry->height; + int x; + + bool found = false; + struct hikari_view *view = NULL; + struct wlr_box *geometry = NULL; + wl_list_for_each (view, &workspace->views, workspace_views) { + if (view == focus_view) { + continue; + } + + geometry = hikari_view_geometry(view); + + int bottom = geometry->y + geometry->height; + + if (geometry->x > lookahead && + ((view_bottom >= geometry->y && geometry->y >= view_geometry->y) || + (bottom >= view_geometry->y && view_geometry->y >= geometry->y))) { + found = true; + x = geometry->x - hikari_configuration.gap - view_geometry->width - + hikari_configuration.border; + break; + } + } + + if (!found) { + int ow, oh; + + wlr_output_transformed_resolution(output, &ow, &oh); + x = ow - view_geometry->width - hikari_configuration.border; + } + + hikari_view_move_absolute(focus_view, x, view_geometry->y); +} + +static void +pin_to_sheet(struct hikari_workspace *workspace, struct hikari_sheet *sheet) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_view_pin_to_sheet(focus_view, sheet); + + if (!hikari_view_is_hidden(focus_view)) { + struct wlr_box *geometry = hikari_view_geometry(focus_view); + struct hikari_output *output = workspace->output; + + hikari_indicator_update_sheet(&hikari_server.indicator, + geometry, + output, + focus_view->sheet, + hikari_configuration.indicator_selected, + hikari_view_is_iconified(focus_view), + hikari_view_is_floating(focus_view)); + } +} + +#define PIN_TO_SHEET(name, sheet) \ + void hikari_workspace_pin_view_to_sheet_##name( \ + struct hikari_workspace *workspace) \ + { \ + pin_to_sheet(workspace, sheet); \ + } + +PIN_TO_SHEET(0, &workspace->sheets[0]) +PIN_TO_SHEET(1, &workspace->sheets[1]) +PIN_TO_SHEET(2, &workspace->sheets[2]) +PIN_TO_SHEET(3, &workspace->sheets[3]) +PIN_TO_SHEET(4, &workspace->sheets[4]) +PIN_TO_SHEET(5, &workspace->sheets[5]) +PIN_TO_SHEET(6, &workspace->sheets[6]) +PIN_TO_SHEET(7, &workspace->sheets[7]) +PIN_TO_SHEET(8, &workspace->sheets[8]) +PIN_TO_SHEET(9, &workspace->sheets[9]) +PIN_TO_SHEET(alternate, workspace->alternate_sheet) +PIN_TO_SHEET(current, workspace->sheet) +PIN_TO_SHEET(next, hikari_sheet_next(workspace->sheet)) +PIN_TO_SHEET(prev, hikari_sheet_prev(workspace->sheet)) +#undef PIN_TO_SHEET + +#define MAXIMIZE(n) \ + void hikari_workspace_toggle_view_##n##_maximize( \ + struct hikari_workspace *workspace) \ + { \ + FOCUS_GUARD(workspace, focus_view) \ + \ + hikari_view_toggle_##n##_maximize(focus_view); \ + } + +MAXIMIZE(full) +MAXIMIZE(vertical) +MAXIMIZE(horizontal) +#undef MAXIMIZE + +void +hikari_workspace_toggle_view_iconified(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_view_toggle_iconified(focus_view); + + struct wlr_box *geometry = hikari_view_geometry(focus_view); + struct hikari_output *output = workspace->output; + + hikari_indicator_update_sheet(&hikari_server.indicator, + geometry, + output, + focus_view->sheet, + hikari_configuration.indicator_selected, + hikari_view_is_iconified(focus_view), + hikari_view_is_floating(focus_view)); +} + +void +hikari_workspace_toggle_view_floating(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_view_toggle_floating(focus_view); + + struct wlr_box *geometry = hikari_view_geometry(focus_view); + struct hikari_output *output = workspace->output; + + hikari_indicator_update_sheet(&hikari_server.indicator, + geometry, + output, + focus_view->sheet, + hikari_configuration.indicator_selected, + hikari_view_is_iconified(focus_view), + hikari_view_is_floating(focus_view)); +} + +void +hikari_workspace_show_iconified_sheet_views(struct hikari_workspace *workspace) +{ + hikari_workspace_clear(workspace); + + struct hikari_view *view = NULL; + wl_list_for_each_reverse (view, &workspace->sheet->views, sheet_views) { + if (hikari_view_is_iconified(view)) { + hikari_view_show(view); + } + } + + hikari_server_cursor_focus(); +} + +void +hikari_workspace_show_all_iconified_views(struct hikari_workspace *workspace) +{ + hikari_workspace_clear(workspace); + + struct hikari_view *view = NULL; + wl_list_for_each_reverse (view, &workspace->output->views, output_views) { + if (hikari_view_is_iconified(view)) { + hikari_view_show(view); + } + } + + hikari_server_cursor_focus(); +} + +void +hikari_workspace_reset_view_geometry(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + hikari_view_reset_geometry(focus_view); +} + +#define VIEW_EXCHANGE(link) \ + void hikari_workspace_exchange_##link##_view( \ + struct hikari_workspace *workspace) \ + { \ + struct hikari_view *focus_view = workspace->focus_view; \ + struct hikari_layout *layout = workspace->sheet->layout; \ + \ + if (focus_view == NULL || layout == NULL || focus_view->tile == NULL || \ + hikari_view_is_dirty(focus_view)) { \ + return; \ + } \ + \ + struct hikari_view *link = hikari_tile_##link##_view(focus_view->tile); \ + \ + assert(!hikari_view_is_hidden(link)); \ + \ + if (focus_view == link || hikari_view_is_dirty(link)) { \ + return; \ + } \ + \ + hikari_view_exchange(focus_view, link); \ + hikari_view_center_cursor(link); \ + } + +VIEW_EXCHANGE(prev) +VIEW_EXCHANGE(next) +#undef VIEW_EXCHANGE + +void +hikari_workspace_exchange_main_layout_view(struct hikari_workspace *workspace) +{ + struct hikari_view *focus_view = workspace->focus_view; + struct hikari_layout *layout = workspace->sheet->layout; + + if (focus_view == NULL || layout == NULL || focus_view->tile == NULL || + hikari_view_is_dirty(focus_view)) { + return; + } + + struct hikari_view *first = hikari_layout_first_view(layout); + + if (focus_view == first || hikari_view_is_hidden(first) || + hikari_view_is_dirty(first)) { + return; + } + + hikari_view_exchange(focus_view, first); + hikari_view_center_cursor(first); +} + +void +hikari_workspace_only_group(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + struct hikari_group *group = focus_view->group; + + struct hikari_view *view = NULL, *view_temp = NULL; + wl_list_for_each_safe (view, view_temp, &workspace->views, workspace_views) { + if (view->group != group) { + hikari_view_hide(view); + } + } + + hikari_server_cursor_focus(); +} + +void +hikari_workspace_show_group(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + struct hikari_group *group = focus_view->group; + + hikari_workspace_clear(workspace); + + struct hikari_view *view = NULL; + wl_list_for_each_reverse (view, &workspace->sheet->views, sheet_views) { + if (view->group == group) { + hikari_view_show(view); + } + } + + hikari_server_cursor_focus(); +} + +void +hikari_workspace_hide_group(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + struct hikari_group *group = focus_view->group; + + struct hikari_view *view = NULL, *view_temp = NULL; + wl_list_for_each_safe ( + view, view_temp, &group->visible_views, visible_group_views) { + hikari_view_hide(view); + } + + hikari_server_cursor_focus(); +} + +void +hikari_workspace_show_all_group_views(struct hikari_workspace *workspace) +{ + FOCUS_GUARD(workspace, focus_view) + + struct hikari_group *group = focus_view->group; + + hikari_workspace_clear(workspace); + + struct hikari_view *view = NULL; + wl_list_for_each_reverse (view, &group->views, group_views) { + hikari_view_show(view); + } + + hikari_server_cursor_focus(); +} + +void +hikari_workspace_show_all_sheet_views(struct hikari_workspace *workspace) +{ + struct hikari_view *view; + struct hikari_sheet *sheet = workspace->sheet; + + hikari_workspace_clear(workspace); + + view = NULL; + wl_list_for_each_reverse (view, &sheet->views, sheet_views) { + hikari_view_show(view); + } + + hikari_server_cursor_focus(); +} + +#undef FOCUS_GUARD diff --git a/src/xdg_view.c b/src/xdg_view.c new file mode 100644 index 0000000..629fb2e --- /dev/null +++ b/src/xdg_view.c @@ -0,0 +1,533 @@ +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +set_title_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xdg_view *xdg_view = + wl_container_of(listener, xdg_view, set_title); + + hikari_view_set_title( + (struct hikari_view *)xdg_view, xdg_view->surface->toplevel->title); +} + +static void +commit_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xdg_view *xdg_view = + wl_container_of(listener, xdg_view, commit); + + struct hikari_view *view = (struct hikari_view *)xdg_view; + + assert(!hikari_view_is_hidden(view)); + + struct wlr_box *geometry = hikari_view_geometry(view); + + assert(view->surface != NULL); + + uint32_t serial = xdg_view->surface->configure_serial; + + if (hikari_view_was_updated(view, serial)) { + switch (view->pending_operation.type) { + case HIKARI_OPERATION_TYPE_TILE: + case HIKARI_OPERATION_TYPE_FULL_MAXIMIZE: + case HIKARI_OPERATION_TYPE_VERTICAL_MAXIMIZE: + case HIKARI_OPERATION_TYPE_HORIZONTAL_MAXIMIZE: + wlr_xdg_toplevel_set_tiled(xdg_view->surface, + WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM); + break; + + case HIKARI_OPERATION_TYPE_RESET: + case HIKARI_OPERATION_TYPE_UNMAXIMIZE: + wlr_xdg_toplevel_set_tiled(xdg_view->surface, WLR_EDGE_NONE); + break; + + case HIKARI_OPERATION_TYPE_RESIZE: + break; + } + hikari_view_commit_pending_operation(view); + } else { + struct wlr_box new_geometry; + wlr_xdg_surface_get_geometry(xdg_view->surface, &new_geometry); + + if (new_geometry.width != geometry->width || + new_geometry.height != geometry->height) { + hikari_view_damage_whole(view); + + geometry->width = new_geometry.width; + geometry->height = new_geometry.height; + + hikari_view_refresh_geometry(view, geometry); + + hikari_view_damage_whole(view); + } else { + pixman_region32_t damage; + pixman_region32_init(&damage); + wlr_surface_get_effective_damage(xdg_view->surface->surface, &damage); + pixman_region32_translate(&damage, geometry->x, geometry->y); + wlr_output_damage_add(view->output->damage, &damage); + pixman_region32_fini(&damage); + } + } +} + +static void +first_map(struct hikari_xdg_view *xdg_view, bool *focus) +{ + assert(xdg_view->surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + + struct hikari_sheet *sheet; + struct hikari_group *group; + struct hikari_view *view = (struct hikari_view *)xdg_view; + struct wlr_box *geometry = &xdg_view->view.geometry; + struct hikari_output *output = hikari_server.workspace->output; + + int screen_width, screen_height; + wlr_output_effective_resolution( + output->output, &screen_width, &screen_height); + + wlr_xdg_surface_get_geometry(xdg_view->surface, geometry); + + const char *app_id = xdg_view->surface->toplevel->app_id; + +#if !defined(NDEBUG) + printf("APP ID %s\n", app_id); +#endif + + int x; + int y; + hikari_configuration_resolve_view_autoconf( + &hikari_configuration, app_id, view, &sheet, &group, &x, &y, focus); + + hikari_view_manage(view, sheet, group); + hikari_view_set_title(view, xdg_view->surface->toplevel->title); + + xdg_view->set_title.notify = set_title_handler; + wl_signal_add( + &xdg_view->surface->toplevel->events.set_title, &xdg_view->set_title); + + hikari_geometry_constrain_position( + geometry, screen_width, screen_height, x, y); + + hikari_view_refresh_geometry(view, geometry); +} + +static struct wlr_surface * +surface_at(struct hikari_view_interface *view_interface, + double ox, + double oy, + double *sx, + double *sy) +{ + struct hikari_xdg_view *xdg_view = (struct hikari_xdg_view *)view_interface; + + struct hikari_view *view = (struct hikari_view *)view_interface; + + struct wlr_box *geometry = hikari_view_geometry(view); + + double x = ox - geometry->x; + double y = oy - geometry->y; + + return wlr_xdg_surface_surface_at(xdg_view->surface, x, y, sx, sy); +} + +static void +map(struct hikari_view *view, bool focus) +{ +#if !defined(NDEBUG) + printf("XDG MAP %p\n", view); +#endif + + struct hikari_xdg_view *xdg_view = (struct hikari_xdg_view *)view; + + view->surface = xdg_view->surface->surface; + + hikari_view_show(view); + + if (focus) { + hikari_view_center_cursor(view); + } + + hikari_server_cursor_focus(); +} + +static void +map_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xdg_view *xdg_view = wl_container_of(listener, xdg_view, map); + + struct hikari_view *view = (struct hikari_view *)xdg_view; + bool focus = false; + + if (view->sheet == NULL) { + first_map(xdg_view, &focus); + } + + map(view, focus); +} + +static void +unmap(struct hikari_view *view) +{ +#if !defined(NDEBUG) + printf("XDG UNMAP %p\n", view); +#endif + + if (!hikari_view_is_hidden(view)) { + hikari_view_hide(view); + hikari_server_cursor_focus(); + } + + struct hikari_xdg_view *xdg_view = (struct hikari_xdg_view *)view; + + if (xdg_view->surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + wl_list_remove(&xdg_view->set_title.link); + } + + view->surface = NULL; +} + +static void +unmap_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xdg_view *xdg_view = wl_container_of(listener, xdg_view, unmap); + + unmap((struct hikari_view *)xdg_view); +} + +static void +activate(struct hikari_view *view, bool active) +{ + struct hikari_xdg_view *xdg_view = (struct hikari_xdg_view *)view; + + if (xdg_view->surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + wlr_xdg_toplevel_set_activated(xdg_view->surface, active); + + hikari_view_damage_whole(view); + } +} + +static uint32_t +resize(struct hikari_view *view, int width, int height) +{ + struct hikari_xdg_view *xdg_view = (struct hikari_xdg_view *)view; + + if (xdg_view->surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + return wlr_xdg_toplevel_set_size(xdg_view->surface, width, height); + } + + return 0; +} + +static void +quit(struct hikari_view *view) +{ + struct hikari_xdg_view *xdg_view = (struct hikari_xdg_view *)view; + + wlr_xdg_toplevel_send_close(xdg_view->surface); +} + +static void +destroy_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xdg_view *xdg_view = + wl_container_of(listener, xdg_view, destroy); + + struct hikari_view *view = (struct hikari_view *)xdg_view; + + if (hikari_view_is_mapped(view)) { + unmap(view); + } + + wl_list_remove(&xdg_view->map.link); + wl_list_remove(&xdg_view->unmap.link); + wl_list_remove(&xdg_view->destroy.link); + wl_list_remove(&xdg_view->new_popup.link); + wl_list_remove(&xdg_view->new_subsurface.link); + wl_list_remove(&xdg_view->request_fullscreen.link); + + hikari_view_fini(view); + hikari_free(xdg_view); +} + +static void +focus(struct hikari_view_interface *view_interface) +{ + struct hikari_view *view = (struct hikari_view *)view_interface; + + hikari_workspace_focus_view(view->sheet->workspace, view); +} + +static void +for_each_surface(struct hikari_view_interface *view_interface, + void (*func)(struct wlr_surface *, int, int, void *), + void *data) +{ + struct hikari_xdg_view *xdg_view = (struct hikari_xdg_view *)view_interface; + + wlr_xdg_surface_for_each_surface(xdg_view->surface, func, data); +} + +static void +destroy_popup_handler(struct wl_listener *listener, void *data) +{ +#if !defined(NDEBUG) + printf("DESTROY POPUP\n"); +#endif + struct hikari_xdg_popup *popup = wl_container_of(listener, popup, destroy); + + hikari_view_child_fini(&popup->view_child); + + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->unmap.link); + wl_list_remove(&popup->map.link); + wl_list_remove(&popup->new_popup.link); + + hikari_free(popup); +} + +static void +xdg_popup_create(struct wlr_xdg_popup *wlr_popup, struct hikari_view *parent); + +static void +new_popup_popup_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xdg_popup *xdg_popup = + wl_container_of(listener, xdg_popup, new_popup); + + struct wlr_xdg_popup *wlr_popup = data; + + xdg_popup_create(wlr_popup, xdg_popup->view_child.parent); +} + +static void +new_popup_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xdg_view *xdg_view = + wl_container_of(listener, xdg_view, new_popup); + + struct wlr_xdg_popup *wlr_popup = data; + + xdg_popup_create(wlr_popup, &xdg_view->view); +} + +static void +popup_map(struct wl_listener *listener, void *data) +{ +#if !defined(NDEBUG) + printf("POPUP MAP\n"); +#endif + + struct hikari_xdg_popup *xdg_popup = + wl_container_of(listener, xdg_popup, map); + + struct hikari_view *parent = xdg_popup->view_child.parent; + + hikari_view_damage_surface(parent, xdg_popup->view_child.surface, true); +} + +static void +popup_unmap(struct wl_listener *listener, void *data) +{ +#if !defined(NDEBUG) + printf("POPUP UNMAP\n"); +#endif + + struct hikari_xdg_popup *xdg_popup = + wl_container_of(listener, xdg_popup, unmap); + + struct hikari_view *parent = xdg_popup->view_child.parent; + + hikari_view_damage_surface(parent, xdg_popup->view_child.surface, true); +} + +static void +popup_unconstrain(struct hikari_xdg_popup *popup) +{ + struct hikari_view *view = popup->view_child.parent; + struct wlr_xdg_popup *wlr_popup = popup->popup; + + struct hikari_output *output = view->output; + + struct wlr_box *geometry = hikari_view_geometry(view); + + struct wlr_box output_toplevel_sx_box = { + .x = output->geometry.x - geometry->x, + .y = output->geometry.y - geometry->y, + .width = output->geometry.width, + .height = output->geometry.height, + }; + + wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); +} + +static void +xdg_popup_create(struct wlr_xdg_popup *wlr_popup, struct hikari_view *parent) +{ + struct hikari_xdg_popup *popup = + hikari_malloc(sizeof(struct hikari_xdg_popup)); + +#if !defined(NDEBUG) + printf("CREATE POPUP\n"); +#endif + + popup->view_child.parent = parent; + popup->popup = wlr_popup; + + wlr_popup->base->surface->data = parent; + + popup->destroy.notify = destroy_popup_handler; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + + popup->new_popup.notify = new_popup_popup_handler; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + + popup->map.notify = popup_map; + wl_signal_add(&wlr_popup->base->events.map, &popup->map); + + popup->unmap.notify = popup_unmap; + wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); + + hikari_view_child_init( + (struct hikari_view_child *)popup, parent, wlr_popup->base->surface); + + // TODO this does not work correctly with multi monitor + popup_unconstrain(popup); +} + +static void +new_subsurface_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xdg_view *xdg_view = + wl_container_of(listener, xdg_view, new_subsurface); + + struct wlr_subsurface *subsurface = data; + + struct hikari_view_subsurface *view_subsurface = + hikari_malloc(sizeof(struct hikari_view_subsurface)); + + hikari_view_subsurface_init(view_subsurface, &xdg_view->view, subsurface); + +#if !defined(NDEBUG) + printf("SUBSURFACE\n"); +#endif +} + +static void +request_fullscreen_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xdg_view *xdg_view = + wl_container_of(listener, xdg_view, request_fullscreen); + + struct wlr_xdg_toplevel_set_fullscreen_event *event = data; + + wlr_xdg_toplevel_set_fullscreen(xdg_view->surface, event->fullscreen); +} + +static void +hide(struct hikari_view *view) +{ + struct hikari_xdg_view *xdg_view = (struct hikari_xdg_view *)view; + wl_list_remove(&xdg_view->commit.link); +} + +static void +show(struct hikari_view *view) +{ + struct hikari_xdg_view *xdg_view = (struct hikari_xdg_view *)view; + + xdg_view->commit.notify = commit_handler; + wl_signal_add(&xdg_view->surface->surface->events.commit, &xdg_view->commit); +} + +static void +constraints(struct hikari_view *view, + int *min_width, + int *min_height, + int *max_width, + int *max_height) +{ + struct hikari_xdg_view *xdg_view = (struct hikari_xdg_view *)view; + struct wlr_xdg_toplevel_state *state = &xdg_view->surface->toplevel->current; + + *min_width = state->min_width > 0 ? state->min_width : 0; + *min_height = state->min_height > 0 ? state->min_height : 0; + *max_width = + state->max_width > 0 ? state->max_width : view->output->geometry.width; + *max_height = + state->max_height > 0 ? state->max_height : view->output->geometry.height; +} + +void +hikari_xdg_view_init(struct hikari_xdg_view *xdg_view, + struct wlr_xdg_surface *xdg_surface, + struct hikari_workspace *workspace) +{ + hikari_view_init(&xdg_view->view, HIKARI_XDG_VIEW, workspace); + +#if !defined(NDEBUG) + printf("NEW XDG %p\n", xdg_view); +#endif + + xdg_view->view.view_interface.surface_at = surface_at; + + wlr_xdg_surface_ping(xdg_surface); + + xdg_view->surface = xdg_surface; + xdg_view->surface->data = xdg_view; + + xdg_view->map.notify = map_handler; + wl_signal_add(&xdg_surface->events.map, &xdg_view->map); + + xdg_view->unmap.notify = unmap_handler; + wl_signal_add(&xdg_surface->events.unmap, &xdg_view->unmap); + + xdg_view->destroy.notify = destroy_handler; + wl_signal_add(&xdg_surface->events.destroy, &xdg_view->destroy); + + xdg_view->new_popup.notify = new_popup_handler; + wl_signal_add(&xdg_surface->events.new_popup, &xdg_view->new_popup); + + xdg_view->new_subsurface.notify = new_subsurface_handler; + wl_signal_add( + &xdg_surface->surface->events.new_subsurface, &xdg_view->new_subsurface); + + xdg_view->request_fullscreen.notify = request_fullscreen_handler; + wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, + &xdg_view->request_fullscreen); + + assert(xdg_view->surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + + xdg_view->view.view_interface.focus = focus; + xdg_view->view.view_interface.for_each_surface = for_each_surface; + xdg_view->view.activate = activate; + xdg_view->view.resize = resize; + xdg_view->view.quit = quit; + xdg_view->view.hide = hide; + xdg_view->view.constraints = constraints; + xdg_view->view.show = show; + xdg_view->view.move = NULL; +} diff --git a/src/xwayland_unmanaged_view.c b/src/xwayland_unmanaged_view.c new file mode 100644 index 0000000..b845922 --- /dev/null +++ b/src/xwayland_unmanaged_view.c @@ -0,0 +1,207 @@ +#ifdef HAVE_XWAYLAND +#include + +#include + +#include +#include +#include +#include + +static bool +was_updated(struct wlr_xwayland_surface *surface, + struct wlr_box *geometry, + struct hikari_output *output) +{ + return !((output->geometry.x + surface->x == geometry->x) && + (output->geometry.y + surface->y == geometry->y) && + (surface->width == geometry->width) && + (surface->height == geometry->height)); +} + +static void +commit_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = + wl_container_of(listener, xwayland_unmanaged_view, commit); + + struct hikari_output *output = xwayland_unmanaged_view->workspace->output; + struct wlr_xwayland_surface *surface = xwayland_unmanaged_view->surface; + struct wlr_box *geometry = &xwayland_unmanaged_view->geometry; + + if (was_updated(surface, geometry, output)) { + hikari_output_add_damage(output, &xwayland_unmanaged_view->geometry); + + geometry->x = surface->x - output->geometry.x; + geometry->y = surface->y - output->geometry.y; + geometry->width = surface->width; + geometry->height = surface->height; + + hikari_output_add_damage(output, geometry); + } else { + pixman_region32_t damage; + pixman_region32_init(&damage); + wlr_surface_get_effective_damage(surface->surface, &damage); + pixman_region32_translate(&damage, geometry->x, geometry->y); + wlr_output_damage_add(output->damage, &damage); + pixman_region32_fini(&damage); + wlr_output_schedule_frame(output->output); + } +} + +static void +map_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = + wl_container_of(listener, xwayland_unmanaged_view, map); + +#if !defined(NDEBUG) + printf("UNMANAGED XWAYLAND MAP %p %d %d\n", + xwayland_unmanaged_view, + xwayland_unmanaged_view->surface->x, + xwayland_unmanaged_view->surface->y); +#endif + + struct wlr_box *geometry = &xwayland_unmanaged_view->geometry; + struct wlr_xwayland_surface *xwayland_surface = + xwayland_unmanaged_view->surface; + struct hikari_output *output = xwayland_unmanaged_view->workspace->output; + + xwayland_unmanaged_view->hidden = false; + + geometry->x = xwayland_surface->x - output->geometry.x; + geometry->y = xwayland_surface->y - output->geometry.y; + geometry->width = xwayland_surface->width; + geometry->height = xwayland_surface->height; + + xwayland_unmanaged_view->commit.notify = commit_handler; + wl_signal_add(&xwayland_surface->surface->events.commit, + &xwayland_unmanaged_view->commit); + + wl_list_insert(&output->unmanaged_xwayland_views, + &xwayland_unmanaged_view->unmanaged_server_views); + + hikari_output_add_damage(output, geometry); +} + +static void +unmap(struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view) +{ + wl_list_remove(&xwayland_unmanaged_view->commit.link); + wl_list_remove(&xwayland_unmanaged_view->unmanaged_server_views); + + xwayland_unmanaged_view->hidden = true; + + hikari_output_add_damage(xwayland_unmanaged_view->workspace->output, + &xwayland_unmanaged_view->geometry); +} + +static void +unmap_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = + wl_container_of(listener, xwayland_unmanaged_view, unmap); + +#if !defined(NDEBUG) + printf("UNMANAGED XWAYLAND UNMAP %p\n", xwayland_unmanaged_view); +#endif + + unmap(xwayland_unmanaged_view); +} + +static void +destroy_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = + wl_container_of(listener, xwayland_unmanaged_view, destroy); + +#if !defined(NDEBUG) + printf("UNMANAGED XWAYLAND DESTROY %p\n", xwayland_unmanaged_view); +#endif + + if (!xwayland_unmanaged_view->hidden) { + unmap(xwayland_unmanaged_view); + hikari_server_cursor_focus(); + } + + wl_list_remove(&xwayland_unmanaged_view->map.link); + wl_list_remove(&xwayland_unmanaged_view->unmap.link); + wl_list_remove(&xwayland_unmanaged_view->destroy.link); + wl_list_remove(&xwayland_unmanaged_view->request_configure.link); + + hikari_free(xwayland_unmanaged_view); +} + +static void +request_configure_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = + wl_container_of(listener, xwayland_unmanaged_view, request_configure); + + struct wlr_xwayland_surface *surface = xwayland_unmanaged_view->surface; + struct wlr_xwayland_surface_configure_event *event = data; + + wlr_xwayland_surface_configure( + surface, event->x, event->y, event->width, event->height); +} + +static struct wlr_surface * +surface_at(struct hikari_view_interface *view_interface, + double lx, + double ly, + double *sx, + double *sy) +{ + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = + (struct hikari_xwayland_unmanaged_view *)view_interface; + + struct wlr_box *geometry = &xwayland_unmanaged_view->geometry; + + double x = lx - geometry->x; + double y = ly - geometry->y; + + return wlr_surface_surface_at( + xwayland_unmanaged_view->surface->surface, x, y, sx, sy); +} + +static void +focus(struct hikari_view_interface *view_interface) +{} + +void +hikari_xwayland_unmanaged_view_init( + struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view, + struct wlr_xwayland_surface *xwayland_surface, + struct hikari_workspace *workspace) +{ + xwayland_unmanaged_view->workspace = workspace; + xwayland_unmanaged_view->view_interface.surface_at = surface_at; + xwayland_unmanaged_view->view_interface.focus = focus; + +#if !defined(NDEBUG) + printf("UNMANAGED XWAYLAND NEW %p\n", xwayland_unmanaged_view); +#endif + + wlr_xwayland_surface_ping(xwayland_surface); + + xwayland_unmanaged_view->surface = xwayland_surface; + xwayland_unmanaged_view->surface->data = + (struct hikari_view_interface *)xwayland_unmanaged_view; + xwayland_unmanaged_view->hidden = true; + + xwayland_unmanaged_view->map.notify = map_handler; + wl_signal_add(&xwayland_surface->events.map, &xwayland_unmanaged_view->map); + + xwayland_unmanaged_view->unmap.notify = unmap_handler; + wl_signal_add( + &xwayland_surface->events.unmap, &xwayland_unmanaged_view->unmap); + + xwayland_unmanaged_view->destroy.notify = destroy_handler; + wl_signal_add( + &xwayland_surface->events.destroy, &xwayland_unmanaged_view->destroy); + + xwayland_unmanaged_view->request_configure.notify = request_configure_handler; + wl_signal_add(&xwayland_surface->events.request_configure, + &xwayland_unmanaged_view->request_configure); +} +#endif diff --git a/src/xwayland_view.c b/src/xwayland_view.c new file mode 100644 index 0000000..195377d --- /dev/null +++ b/src/xwayland_view.c @@ -0,0 +1,417 @@ +#ifdef HAVE_XWAYLAND +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static uint32_t +resize(struct hikari_view *view, int width, int height) +{ + struct hikari_xwayland_view *xwayland_view = + (struct hikari_xwayland_view *)view; + + struct wlr_box *geometry = hikari_view_geometry(view); + struct hikari_output *output = view->output; + + wlr_xwayland_surface_configure(xwayland_view->surface, + output->geometry.x + geometry->x, + output->geometry.y + geometry->y, + width, + height); + + return 0; +} + +static void +move(struct hikari_view *view, int x, int y) +{ + struct hikari_xwayland_view *xwayland_view = + (struct hikari_xwayland_view *)view; + + struct wlr_box *geometry = hikari_view_geometry(view); + struct hikari_output *output = view->output; + + wlr_xwayland_surface_configure(xwayland_view->surface, + output->geometry.x + x, + output->geometry.y + y, + geometry->width, + geometry->height); +} + +static bool +was_updated(struct hikari_xwayland_view *xwayland_view) +{ + struct hikari_view *view = (struct hikari_view *)xwayland_view; + + struct wlr_xwayland_surface *surface = xwayland_view->surface; + struct wlr_box *pending_geometry = &view->pending_operation.geometry; + + return hikari_view_is_dirty(view) && + (surface->width == pending_geometry->width || + surface->height == pending_geometry->height); +} + +static bool +was_moved(struct wlr_xwayland_surface *surface, + struct wlr_box *geometry, + struct hikari_output *output) +{ + return !((output->geometry.x + surface->x == geometry->x) && + (output->geometry.y + surface->y == geometry->y)); +} + +static void +commit_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, commit); + + struct hikari_view *view = (struct hikari_view *)xwayland_view; + struct wlr_box *geometry = hikari_view_geometry(view); + struct hikari_output *output = view->output; + struct wlr_xwayland_surface *surface = xwayland_view->surface; + + if (was_updated(xwayland_view)) { + hikari_view_commit_pending_operation(view, geometry); + + geometry = hikari_view_geometry(view); + + wlr_xwayland_surface_configure(surface, + output->geometry.x + geometry->x, + output->geometry.y + geometry->y, + geometry->width, + geometry->height); + } else if (was_moved(surface, geometry, output)) { + hikari_view_damage_whole(view); + hikari_indicator_damage(&hikari_server.indicator, view); + + geometry->x = surface->x - output->geometry.x; + geometry->y = surface->y - output->geometry.y; + + hikari_view_damage_whole(view); + } else { + pixman_region32_t damage; + pixman_region32_init(&damage); + wlr_surface_get_effective_damage(surface->surface, &damage); + pixman_region32_translate(&damage, geometry->x, geometry->y); + wlr_output_damage_add(output->damage, &damage); + pixman_region32_fini(&damage); + wlr_output_schedule_frame(output->output); + } +} + +static void +set_title_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_title); + + struct hikari_view *view = (struct hikari_view *)xwayland_view; + + hikari_view_set_title(view, xwayland_view->surface->title); +} + +static void +first_map(struct hikari_xwayland_view *xwayland_view) +{ + struct hikari_view *view = (struct hikari_view *)xwayland_view; + struct hikari_output *output = hikari_server.workspace->output; + + view->border = HIKARI_BORDER_INACTIVE; + + struct hikari_sheet *sheet; + struct hikari_group *group; + int x; + int y; + bool focus; + + hikari_configuration_resolve_view_autoconf(&hikari_configuration, + xwayland_view->surface->class, + view, + &sheet, + &group, + &x, + &y, + &focus); + + if (x == -1) { + x = hikari_server.cursor->x - output->geometry.x; + } + + if (y == -1) { + y = hikari_server.cursor->y - output->geometry.y; + } + + view->geometry.width = xwayland_view->surface->width; + view->geometry.height = xwayland_view->surface->height; + + xwayland_view->commit.notify = commit_handler; + wl_signal_add( + &xwayland_view->surface->surface->events.commit, &xwayland_view->commit); + + hikari_view_set_title(view, xwayland_view->surface->title); + hikari_view_manage(view, sheet, group); + + int screen_width, screen_height; + wlr_output_effective_resolution( + output->output, &screen_width, &screen_height); + + hikari_geometry_constrain_position( + &view->geometry, screen_width, screen_height, x, y); + + wlr_xwayland_surface_configure(xwayland_view->surface, + output->geometry.x + view->geometry.x, + output->geometry.y + view->geometry.y, + view->geometry.width, + view->geometry.height); +} + +static void +map(struct hikari_view *view) +{ +#if !defined(NDEBUG) + printf("XWAYLAND MAP %p\n", view); +#endif + + struct hikari_xwayland_view *xwayland_view = + (struct hikari_xwayland_view *)view; + + view->surface = xwayland_view->surface->surface; + view->surface->data = (struct hikari_view_interface *)view; + + /* if (view->sheet == view->sheet->workspace->sheet || */ + /* view->sheet == &view->sheet->workspace->sheets[0]) { */ + hikari_view_show(view); + hikari_server_cursor_focus(); + /* } */ +} + +static void +map_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, map); + + struct hikari_view *view = (struct hikari_view *)xwayland_view; + + if (view->sheet == NULL) { + first_map(xwayland_view); + } + + map(view); +} + +static void +unmap(struct hikari_view *view) +{ +#if !defined(NDEBUG) + printf("XWAYLAND UNMAP %p\n", view); +#endif + + if (!hikari_view_is_hidden(view)) { + hikari_view_hide(view); + hikari_server_cursor_focus(); + } + + struct hikari_xwayland_view *xwayland_view = + (struct hikari_xwayland_view *)view; + + wl_list_remove(&xwayland_view->commit.link); + + view->surface = NULL; +} + +static void +unmap_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, unmap); + + unmap((struct hikari_view *)xwayland_view); +} + +static void +destroy_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, destroy); + +#if !defined(NDEBUG) + printf("XWAYLAND DESTROY %p\n", xwayland_view); +#endif + + struct hikari_view *view = (struct hikari_view *)xwayland_view; + + if (hikari_view_is_mapped(view)) { + unmap(view); + } + + hikari_view_fini(&xwayland_view->view); + + wl_list_remove(&xwayland_view->map.link); + wl_list_remove(&xwayland_view->unmap.link); + wl_list_remove(&xwayland_view->destroy.link); + wl_list_remove(&xwayland_view->request_configure.link); + wl_list_remove(&xwayland_view->set_title.link); + + hikari_free(xwayland_view); +} + +static void +request_configure_handler(struct wl_listener *listener, void *data) +{ + struct hikari_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, request_configure); + + struct wlr_xwayland_surface *xwayland_surface = xwayland_view->surface; + struct wlr_xwayland_surface_configure_event *event = data; + +#if !defined(NDEBUG) + printf("XWAYLAND CONFIGURE %p %d %d\n", xwayland_view, event->x, event->y); +#endif + + struct wlr_box geometry = { + .x = event->x, .y = event->y, .width = event->width, .height = event->height + }; + + struct hikari_sheet *sheet = xwayland_view->view.sheet; + + int screen_width, screen_height; + if (sheet != NULL) { + wlr_output_effective_resolution( + xwayland_view->view.output->output, &screen_width, &screen_height); + } else { + wlr_output_effective_resolution( + hikari_server.workspace->output->output, &screen_width, &screen_height); + } + + hikari_geometry_constrain_position( + &geometry, screen_width, screen_height, event->x, event->y); + + wlr_xwayland_surface_configure(xwayland_surface, + geometry.x, + geometry.y, + geometry.width, + geometry.height); +} + +static void +activate(struct hikari_view *view, bool active) +{ + struct hikari_xwayland_view *xwayland_view = + (struct hikari_xwayland_view *)view; + + struct wlr_xwayland_surface *xwayland_surface = xwayland_view->surface; + + wlr_xwayland_surface_activate(xwayland_surface, active); + wlr_xwayland_set_seat(hikari_server.xwayland, hikari_server.seat); +} + +static void +quit(struct hikari_view *view) +{ + struct hikari_xwayland_view *xwayland_view = + (struct hikari_xwayland_view *)view; + + wlr_xwayland_surface_close(xwayland_view->surface); +} + +static struct wlr_surface * +surface_at(struct hikari_view_interface *view_interface, + double ox, + double oy, + double *sx, + double *sy) +{ + struct hikari_view *view = (struct hikari_view *)view_interface; + + struct wlr_box *geometry = hikari_view_geometry(view); + + double x = ox - geometry->x; + double y = oy - geometry->y; + + return wlr_surface_surface_at(view->surface, x, y, sx, sy); +} + +static void +focus(struct hikari_view_interface *view_interface) +{ + struct hikari_view *view = (struct hikari_view *)view_interface; + + hikari_workspace_focus_view(view->sheet->workspace, view); +} + +static void +for_each_surface(struct hikari_view_interface *view_interface, + void (*func)(struct wlr_surface *, int, int, void *), + void *data) +{ + struct hikari_view *view = (struct hikari_view *)view_interface; + + wlr_surface_for_each_surface(view->surface, func, data); +} + +static void +hide(struct hikari_view *view) +{} + +static void +show(struct hikari_view *view) +{} + +void +hikari_xwayland_view_init(struct hikari_xwayland_view *xwayland_view, + struct wlr_xwayland_surface *xwayland_surface, + struct hikari_workspace *workspace) +{ + hikari_view_init(&xwayland_view->view, HIKARI_XWAYLAND_VIEW, workspace); + + xwayland_view->view.view_interface.surface_at = surface_at; + xwayland_view->view.view_interface.focus = focus; + xwayland_view->view.view_interface.for_each_surface = for_each_surface; + + wlr_xwayland_surface_ping(xwayland_surface); + +#if !defined(NDEBUG) + printf("XWAYLAND NEW %p\n", xwayland_view); +#endif + + xwayland_view->surface = xwayland_surface; + + xwayland_view->map.notify = map_handler; + wl_signal_add(&xwayland_surface->events.map, &xwayland_view->map); + + xwayland_view->unmap.notify = unmap_handler; + wl_signal_add(&xwayland_surface->events.unmap, &xwayland_view->unmap); + + xwayland_view->destroy.notify = destroy_handler; + wl_signal_add(&xwayland_surface->events.destroy, &xwayland_view->destroy); + + xwayland_view->request_configure.notify = request_configure_handler; + wl_signal_add(&xwayland_surface->events.request_configure, + &xwayland_view->request_configure); + + xwayland_view->set_title.notify = set_title_handler; + wl_signal_add(&xwayland_surface->events.set_title, &xwayland_view->set_title); + + xwayland_view->view.activate = activate; + xwayland_view->view.resize = resize; + xwayland_view->view.move = move; + xwayland_view->view.quit = quit; + xwayland_view->view.hide = hide; + xwayland_view->view.show = show; +} +#endif