mirror of
https://cagebreak.project-repo.co/cagebreak.git
synced 2026-08-09 07:09:11 +02:00
Release 2.0.0
This is a major release and includes BREAKING changes! Breaking Changes: * More intuitive switch of focus, next, prev and exchange (#20, 40 in Bugs.md) Cagebreak will probably still feel broadly the same though. * Remove -r flag - This is replaced by output configuration. * Socket disabled by default - The socket is enabled by invoking Cagebreak with the -e flag. * Socket permissions restricted to the user of the Cagebreak process (700) This may require modification of scripts interacting with the socket with different UIDs than the UID of the Cagebreak process. * Rename "Current frame" indicator to "Current tile" Changelog: * Move to wlr_scene * Add events displaying change over the socket * this enables scripting tools * Add support for scaling outputs (thanks to Oliver Friedmann) * Remove -r flag * Add rotation to output configuration * Add cursor enable|disable flag * Add input keyboard configuration * Custom path flag for configuration file * Restrict socket permissions to the user running cagebreak (700) * Fix Issue #31 (resolve some terminology) 38 in Bugs.md * Fix Issue #30 - 39 in Bugs.md * Fix Issue #20 - 40 in Bugs.md * Fix Issue #12 - 41 in Bugs.md (dump + events over socket) * Fix Bugs found via scan-build static analysis (42 - 45 in Bugs.md) * Fix Issue #33 - 46 in Bugs.md * Fix Issue #32 - 47 in Bugs.md * Fix Issue #16 - 48 in Bugs.md * Fix Issue #3 - 49 in Bugs.md (disabling keybinding interpretation for specific keyboards is now possible) * Fix Issue #26 - 50 in Bugs.md * Fix Issue #7 - 51 in Bugs.md (Change cursor to square while Cagebreak is waiting for a key) * Fix config bug for some commands - 52 in Bugs.md * Fix Issue #35 - 53 in Bugs.md (update to wlroots 0.16.1) * Improve FAQ.md (related to some Issues) * Disable outputs when unable to set any mode (crashed previously) * Add Code of Conduct * Print version number on startup
This commit is contained in:
parent
728c2e4ec8
commit
6dbcb7eda9
64 changed files with 4762 additions and 2595 deletions
462
Bugs.md
462
Bugs.md
|
|
@ -591,7 +591,6 @@ without xwayland has been added to the release checklist.
|
||||||
* github issue number: #23
|
* github issue number: #23
|
||||||
* Fixed: 1.9.0
|
* Fixed: 1.9.0
|
||||||
|
|
||||||
```
|
|
||||||
Similar to djpohly/dwl#177. Xwayland works fine elsewhere: cage, hikari, kwinft, labwc, phoc, river, sway, wayfire.
|
Similar to djpohly/dwl#177. Xwayland works fine elsewhere: cage, hikari, kwinft, labwc, phoc, river, sway, wayfire.
|
||||||
```
|
```
|
||||||
$ cagebreak
|
$ cagebreak
|
||||||
|
|
@ -618,7 +617,6 @@ wayland-protocols-1.25
|
||||||
xwayland-devel-21.0.99.1.171
|
xwayland-devel-21.0.99.1.171
|
||||||
wlroots-0.15.1
|
wlroots-0.15.1
|
||||||
```
|
```
|
||||||
```
|
|
||||||
|
|
||||||
## Issue 34
|
## Issue 34
|
||||||
|
|
||||||
|
|
@ -652,3 +650,463 @@ This issue was not present in earlier releases.
|
||||||
Cagebreak 1.9.0 broke the screen/movetoscreen functionality.
|
Cagebreak 1.9.0 broke the screen/movetoscreen functionality.
|
||||||
This issue was not present in earlier releases.
|
This issue was not present in earlier releases.
|
||||||
|
|
||||||
|
## Issue 38
|
||||||
|
|
||||||
|
* github issue number: #31
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Cagebreak terminology was unclear up to and including 1.9.1.
|
||||||
|
|
||||||
|
The following are extracts from the github issue discussion:
|
||||||
|
|
||||||
|
kinleyd:
|
||||||
|
```
|
||||||
|
This isn't an issue per se, but concerns cagebreak terminology.
|
||||||
|
I won't try to cover the entire range of terms used but stick
|
||||||
|
to the ones that concern me at the moment:
|
||||||
|
|
||||||
|
OK, so I get the use of the terms 'output', 'workspace' and 'tile'.
|
||||||
|
|
||||||
|
So my question: In cagebreak, what exactly is a 'screen'? At the
|
||||||
|
moment, the use of nextscreen, prevscreen, movetoscreen,
|
||||||
|
movetonextscreen, movetoprevscreen all seem to suggest that
|
||||||
|
'screen' is the same as 'output'. If so, shouldn't all the
|
||||||
|
aforementioned commands have screen replaced with output?
|
||||||
|
eg. nextoutput, prevoutput, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
project-repo
|
||||||
|
```
|
||||||
|
You make an interesting point.
|
||||||
|
|
||||||
|
We have considered this and the tricky bit is that both output and *screen are
|
||||||
|
defined cagebreak commands. It would be difficult to merge these two
|
||||||
|
commands in a reasonable way (even with the freedom of breaking changes).
|
||||||
|
|
||||||
|
Additionally, there is a sort of informal symmetry between output and input
|
||||||
|
we would like to maintain.
|
||||||
|
|
||||||
|
Our current conclusion is that we will leave the commands unmodified.
|
||||||
|
The now ex-post defined distinction between output and *screen is that
|
||||||
|
the *screen stuff works with the natural number uniquely identifying the
|
||||||
|
output within a given cagebreak session (but not across sessions, "nextscreen"
|
||||||
|
and "prevscreen" do so internally by in-/decrementing this number), whereas
|
||||||
|
output always deals with an output name as does input with an input name.
|
||||||
|
|
||||||
|
Please feel free to point out any other inconsistencies in cagebreak or better
|
||||||
|
solutions than outlined above in this issue.
|
||||||
|
|
||||||
|
You can post your suggestions here.
|
||||||
|
|
||||||
|
We will close this issue once 2.0.0 is released where the points mentioned above
|
||||||
|
will be outlined in the documentation. Please leave it open in
|
||||||
|
the mean time. Afterwards, you may open a new issue.
|
||||||
|
|
||||||
|
cheers
|
||||||
|
project-repo
|
||||||
|
```
|
||||||
|
|
||||||
|
kinleyd:
|
||||||
|
```
|
||||||
|
> Our current conclusion is that we will leave the commands unmodified.
|
||||||
|
> The now ex-post defined distinction between output and *screen is
|
||||||
|
> that the *screen stuff works with the natural number uniquely
|
||||||
|
> identifying the output within a given cagebreak session (but not
|
||||||
|
> across sessions, "nextscreen" and "prevscreen" do so internally
|
||||||
|
> by in-/decrementing this number), whereas output always deals
|
||||||
|
> with an output name as does input with an input name.
|
||||||
|
|
||||||
|
That makes perfect sense. Additionally, I also realize that there
|
||||||
|
is the virtual display for which the concept of 'screen' as
|
||||||
|
distinct from 'output' is necessary as a screen could then be
|
||||||
|
a fraction of an output, or combine multiple outputs.
|
||||||
|
|
||||||
|
> We will close this issue once 2.0.0 is released where the points
|
||||||
|
> mentioned above will be outlined in the documentation. Please
|
||||||
|
> leave it open in the mean time. Afterwards, you may open a new issue.
|
||||||
|
|
||||||
|
Hey, I look forward to version 2.0.0!
|
||||||
|
```
|
||||||
|
|
||||||
|
project-repo:
|
||||||
|
```
|
||||||
|
We have now modified the cagebreak-config man page on the development
|
||||||
|
branch to clarify the relationship of output and the screen family of
|
||||||
|
commands.
|
||||||
|
|
||||||
|
Please let us know if anything can be improved in that regard.
|
||||||
|
|
||||||
|
cheers
|
||||||
|
project-repo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Issue 39
|
||||||
|
|
||||||
|
* github issue number: #30
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Cagebreak 1.9.1 had an issue where clicks did not evoke
|
||||||
|
any response under some very specific circumstances.
|
||||||
|
|
||||||
|
This was remediated by switching Cagebreak to `wlr_scene`.
|
||||||
|
|
||||||
|
There was extensive discussion inside the github issue
|
||||||
|
but this is omitted here due to irrelevance.
|
||||||
|
|
||||||
|
## Issue 40
|
||||||
|
|
||||||
|
* github issue number: #20
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
nor-0 proposed a more intuitive way to switch focus. This
|
||||||
|
is a breaking change and as such is introduced on a major
|
||||||
|
release.
|
||||||
|
|
||||||
|
Details are described in the github issue discussion, which
|
||||||
|
is reproduced here:
|
||||||
|
|
||||||
|
nor-0:
|
||||||
|
```
|
||||||
|
Hello,
|
||||||
|
|
||||||
|
I think switching focus is a bit confusing in some situations.
|
||||||
|
When focus is switched in a direction, it can end up diagonally
|
||||||
|
instead of the frame next to the previously focused one.
|
||||||
|
The minimal example for this is with four frames, but it
|
||||||
|
also applies in layouts with more than four of them.
|
||||||
|
|
||||||
|
Let´s say I press the keybindings for these actions:
|
||||||
|
`vsplit`, `hsplit`, `focusright`, `hsplit`.
|
||||||
|
This leaves me with a layout of four simple quarters:
|
||||||
|
|
||||||
|
> +-----+-----+
|
||||||
|
> | 1 | 2 |
|
||||||
|
> +-----+-----+
|
||||||
|
> | 3 | 4 |
|
||||||
|
> +-----+-----+
|
||||||
|
|
||||||
|
Now, using `focuslefton` frame 2 brings me to frame 3 instead
|
||||||
|
of one. Likewise, `focusright` on frame 3 brings me to frame
|
||||||
|
2 instead of frame 4.
|
||||||
|
|
||||||
|
As soon as I resize a width, switching works as expected and
|
||||||
|
desired. The oddness however reappears whenever the vertical
|
||||||
|
frame edges are realigned.
|
||||||
|
|
||||||
|
The same trouble happens when creating the layout with the
|
||||||
|
keybindings for `hsplit`, `vsplit`, `focusdown`, `vsplit`.
|
||||||
|
Now, focusing up from frame 3 brings me two frame 2 instead
|
||||||
|
of 1, and same with the symmetrical opposite again
|
||||||
|
(2 downwards is 3 instead of 4). Now resizing heights is what
|
||||||
|
helps until the horizontal edges are realigned.
|
||||||
|
|
||||||
|
As said before, similar things occur with more complex layouts.
|
||||||
|
I use those much less frequently of course, but intuitive
|
||||||
|
navigation is even more important then. In these two minimal
|
||||||
|
examples, reaching the frames 1 and 4 is also often more
|
||||||
|
tedious then necessary.
|
||||||
|
|
||||||
|
I understand this is due to the way the frames were created
|
||||||
|
and makes some sense in this regard. However, unless there
|
||||||
|
is some reason to keep this behavior (which disappears by
|
||||||
|
some resize options anyways), I´d suggest to change it to
|
||||||
|
be more intuitive.
|
||||||
|
|
||||||
|
I imagine an approach for this could be to determine the
|
||||||
|
frames center point and then looking for an adjacent frame
|
||||||
|
in horizontal (vertical) direction from there when
|
||||||
|
`focusright` or `focusleft` (`focusdown` or `focusup`) is used.
|
||||||
|
|
||||||
|
(Everything said about `focus[direction]` also applies to
|
||||||
|
`exchange[direction]`.)
|
||||||
|
```
|
||||||
|
|
||||||
|
project-repo:
|
||||||
|
```
|
||||||
|
Thanks for the suggestion! Yes, I agree that the current implementation
|
||||||
|
is not a very satisfying solution... The reason it is handled like this
|
||||||
|
is simply because it was easiest to implement. Fortunately however,
|
||||||
|
changing this should not require too much coding since the
|
||||||
|
determination of the tile to jump to is handled globally by a single
|
||||||
|
function for each direction.
|
||||||
|
|
||||||
|
I like your idea with considering the center of the frame and then
|
||||||
|
looking for the one adjacent to it which is at the height of the center,
|
||||||
|
since such a frame is always guaranteed to exist (unless we are at the
|
||||||
|
edge of the screen in which case there is nothing to jump to).
|
||||||
|
Of course, this would have to involve some kind of tie-breaking when
|
||||||
|
two adjacent frames share a border at the height of the center (which is
|
||||||
|
bound to occur often if you never resize the tiles). I'm currently
|
||||||
|
leaning towards simply taking the top/left one in this case (or some
|
||||||
|
similar scheme) but if anyone else has thoughts on this, we'd be glad to
|
||||||
|
hear!
|
||||||
|
|
||||||
|
Some notes:
|
||||||
|
|
||||||
|
* This would be a breaking change, so the version number would jump to 2.0.0.
|
||||||
|
* Also, due to limited developer capacity at the moment and
|
||||||
|
the ongoing porting of cagebreak to the wlr_scene API it may be a
|
||||||
|
while until we are able to implement this, so bear with us!
|
||||||
|
|
||||||
|
cheers
|
||||||
|
project-repo
|
||||||
|
```
|
||||||
|
|
||||||
|
project-repo:
|
||||||
|
```
|
||||||
|
We pushed some code to development implementing this in `ed5cca9`.
|
||||||
|
Sorry for not writing this much sooner.
|
||||||
|
|
||||||
|
To anyone who reads this: This feature will remain on the development
|
||||||
|
branch for some time, partially due to some unresolved xwayland and
|
||||||
|
wlr_scene bugs and partially to allow breaking changes. So if anyone
|
||||||
|
would like to see something changed, please feel free to let us know!
|
||||||
|
|
||||||
|
cheers
|
||||||
|
project-repo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Issue 41
|
||||||
|
|
||||||
|
* github issue number: #12
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
kinleyd raised the issue of changing the focussed screen
|
||||||
|
on mouse hover. The Cagebreak philosophy is completely
|
||||||
|
keyboard-oriented and this feature request was denied.
|
||||||
|
However, this line of inquiry led to the idea of implementing
|
||||||
|
an ipc socket which would report key events and an information
|
||||||
|
dump functionality which would enable almost arbitrary scripting.
|
||||||
|
|
||||||
|
Related Material:
|
||||||
|
- [cagebreak-config man page](man/cagebreak-config.md)
|
||||||
|
- [cagebreak-socket man page](man/cagebreak-socket.md)
|
||||||
|
- [scripting examples](https://github.com/project-repo/cagebreak/tree/development/example_scripts)
|
||||||
|
|
||||||
|
The github issue discussion is partially reproduced below:
|
||||||
|
|
||||||
|
kinleyd:
|
||||||
|
```
|
||||||
|
While fully recognizing - and appreciating - Cagebreak's keyboard orientation, would it be possible to accommodate support for screen change on mouse over? This would help avoid a series of key presses on multi-screen setups and improve overall ergonomics.
|
||||||
|
|
||||||
|
Thanks.
|
||||||
|
```
|
||||||
|
|
||||||
|
project-repo:
|
||||||
|
```
|
||||||
|
As you correctly recognised, cagebreak is designed to be completely
|
||||||
|
keyboard-oriented. However, I understand that for the multi-monitor
|
||||||
|
setup you describe it may be useful to have some kind of
|
||||||
|
mouse-compositor interaction. To accommodate this, we are considering
|
||||||
|
implementing a command which allows to obtain the current cagebreak
|
||||||
|
state from the ipc socket, including the current cursor position.
|
||||||
|
That way, by implementing a script which listens for cursor events
|
||||||
|
and acts accordingly, it should be possible to implement the behaviour
|
||||||
|
you describe externally (provided #11 is implemented as intended).
|
||||||
|
What do you think?
|
||||||
|
|
||||||
|
If you have any other ideas, don't hesitate to let us know!
|
||||||
|
|
||||||
|
cheers
|
||||||
|
project-repo
|
||||||
|
```
|
||||||
|
|
||||||
|
project-repo
|
||||||
|
```
|
||||||
|
There has been much talk about #12 and how it would improve all sorts of
|
||||||
|
use cases, once implemented. While much remains to be determined, we would
|
||||||
|
like to give an update.
|
||||||
|
|
||||||
|
First, #12 is secondary in priority to #11, which is presumably easier
|
||||||
|
to implement and much clearer in its scope.
|
||||||
|
|
||||||
|
Once implemented, cagebreak will communicate the state of at least the
|
||||||
|
following internal properties:
|
||||||
|
|
||||||
|
* [ ] Current cursor position (allowing setting of the current screen
|
||||||
|
as per navigating screens question and suggestion #11
|
||||||
|
* [ ] IDs for graphical programs along with position on screen
|
||||||
|
* [ ] IDs for tiles on particular screens
|
||||||
|
* [ ] Currently focussed tile, screen, etc.
|
||||||
|
* [ ] Events such as "new window", "screen split", etc.
|
||||||
|
|
||||||
|
In which format this information will be provided is yet to be
|
||||||
|
determined, but we are considering at least the following:
|
||||||
|
|
||||||
|
* full blob of data upon request via ipc socket
|
||||||
|
* partial information on events
|
||||||
|
|
||||||
|
To improve user experience, the release of #12 will include examples of
|
||||||
|
how to use the new API over the socket for real world use cases,
|
||||||
|
analogous to the current example config.
|
||||||
|
|
||||||
|
* [ ] Example script "Current screen follows cursor on click"
|
||||||
|
* [ ] Example script "Setup multi screen environment with graphical programs on
|
||||||
|
specific tiles"
|
||||||
|
|
||||||
|
Note that this is just the current state of planning,
|
||||||
|
the final properties of cagebreak are specified in the man pages upon
|
||||||
|
release and cagebreak is provided as is, as specified in the LICENSE.
|
||||||
|
|
||||||
|
cheers
|
||||||
|
project-repo
|
||||||
|
```
|
||||||
|
|
||||||
|
project-repo:
|
||||||
|
```
|
||||||
|
Some but not all features mentioned in our previous comment have now been implemented on
|
||||||
|
the development branch.
|
||||||
|
|
||||||
|
Information on current events is provided through the ipc socket
|
||||||
|
now. The types of events which are reported are still subject to change and the
|
||||||
|
feature is not yet documented but you may check it out nonetheless. Let
|
||||||
|
us know if there is any additional information you would like to obtain
|
||||||
|
for the different events.
|
||||||
|
|
||||||
|
Current format displayed on the socket is as follows:
|
||||||
|
|
||||||
|
'cg-ipc<4 bytes denoting message length><keyword denoting the type of the event>(<property1>:<value1>,<property2>:<value2>,...,<propertyn>:<valuen>)'
|
||||||
|
|
||||||
|
Consider it a proof of concept.
|
||||||
|
|
||||||
|
A full dump of the current cagebreak state is not yet possible.
|
||||||
|
|
||||||
|
Happy new year to you too!
|
||||||
|
cheers
|
||||||
|
project-repo
|
||||||
|
```
|
||||||
|
|
||||||
|
project-repo:
|
||||||
|
```
|
||||||
|
We pushed some code to development implementing the dump
|
||||||
|
functionality. This should contain the information you require since it
|
||||||
|
gives you the view currently focussed by the pointer from which you can
|
||||||
|
deduce the output the cursor is currently hovering over by looking up
|
||||||
|
the output on which the view is placed. Note that this only works when
|
||||||
|
the cursor is actually hovering over a view (i.e. not when it is on an
|
||||||
|
empty tile). Alternatively, you could read the cursor coordinates and
|
||||||
|
compare these with the coordinates of the different outputs, though atm
|
||||||
|
the coordinates of the outputs are not dumped (we aim to add this later).
|
||||||
|
|
||||||
|
To anyone who reads this: This feature will remain on the development
|
||||||
|
branch for some time, partially due to some unresolved xwayland and
|
||||||
|
wlr_scene bugs and partially to allow breaking changes
|
||||||
|
(removing/changing data outputted by dump and other information sent by
|
||||||
|
IPC). So if anyone would like to see something changed, please feel free
|
||||||
|
to let us know (either by posting here or opening a new issue)!
|
||||||
|
|
||||||
|
cheers
|
||||||
|
project-repo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Issue 42
|
||||||
|
|
||||||
|
* github issue number: N/A
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
When Cagebreak exited due to an error on startup, it was possible for
|
||||||
|
memory leaks to occur since `server.modes` was not freed correctly. The
|
||||||
|
same applies to the variable `name` in `input_manager.c`.
|
||||||
|
|
||||||
|
This bug was found using the scan-build utility.
|
||||||
|
|
||||||
|
## Issue 43
|
||||||
|
|
||||||
|
* github issue number: N/A
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
In `keybinding_move_view_to_cycle_output`, it was previously possible for a
|
||||||
|
null pointer dereference to occur, when no view to be moved was focussed.
|
||||||
|
|
||||||
|
This bug was found using the scan-build utility.
|
||||||
|
|
||||||
|
## Issue 44
|
||||||
|
|
||||||
|
* github issue number: N/A
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
`cg_input_reset_libinput_device` was removed as it was unused code.
|
||||||
|
|
||||||
|
This bug was found using the scan-build utility.
|
||||||
|
|
||||||
|
## Issue 45
|
||||||
|
|
||||||
|
* github issue number: N/A
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Various memory leaks in `parse_message_config`, `full_screen_workspace`
|
||||||
|
and `server_show_info` were fixed.
|
||||||
|
|
||||||
|
This bug was found using the scan-build utility.
|
||||||
|
|
||||||
|
## Issue 46
|
||||||
|
|
||||||
|
* github issue number: #33
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Cagebrak did not state in its FAQ how to use letters not found
|
||||||
|
on the current keyboards alphabet.
|
||||||
|
|
||||||
|
## Issue 47
|
||||||
|
|
||||||
|
* github issue number: #32
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Cagebreak did not state in its FAQ how programs can be launched.
|
||||||
|
|
||||||
|
## Issue 48
|
||||||
|
|
||||||
|
* github issue number: #16
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Cagebreak cursor sizes varied under some environmental circumstances.
|
||||||
|
Investigations showed that the cursor is set by the respective application
|
||||||
|
but environment variables are usually respected. This is now documented in
|
||||||
|
the FAQ.
|
||||||
|
|
||||||
|
## Issue 49
|
||||||
|
|
||||||
|
* github issue number: #3
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Cagebreak used to have no way to disable the interpretation of keybindings
|
||||||
|
for a specific keyboard, which was cumbersome for some applications. This
|
||||||
|
feature is now available
|
||||||
|
|
||||||
|
## Issue 50
|
||||||
|
|
||||||
|
* github issue number: #26
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Cagebreak used to have no way to hide the cursor. This is now possible.
|
||||||
|
|
||||||
|
## Issue 51
|
||||||
|
|
||||||
|
* github issue number: #7
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Cagebreak used to not change the cursor while waiting for a key. The cursor
|
||||||
|
is now a square in this state.
|
||||||
|
|
||||||
|
## Issue 52
|
||||||
|
|
||||||
|
* github issue number: N/A
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Cagebreak did not correctly apply some simple commands if they were simply
|
||||||
|
written in the configuration file.
|
||||||
|
|
||||||
|
Consider the example config file:
|
||||||
|
|
||||||
|
```
|
||||||
|
quit
|
||||||
|
```
|
||||||
|
|
||||||
|
Under these circumstances Cagebreak did not quit right after startup.
|
||||||
|
|
||||||
|
## Issue 53
|
||||||
|
|
||||||
|
* github issue number: #35
|
||||||
|
* Fixed: 2.0.0
|
||||||
|
|
||||||
|
Cagebreak did not build with wlroots 0.16.1 and this was adjusted.
|
||||||
|
|
|
||||||
15
CODE_OF_CONDUCT.md
Normal file
15
CODE_OF_CONDUCT.md
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Code of Conduct
|
||||||
|
|
||||||
|
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, and other activities.
|
||||||
|
|
||||||
|
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
||||||
|
|
||||||
|
This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting project-repo (See [SECURITY.md](SECURITY.md) for an e-mail contact.).
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from Curl, available at https://curl.se/docs/code-of-conduct.html and transitively the Contributor Covenant, version 1.1.0, available at https://contributor-covenant.org/version/1/1/0/
|
||||||
42
Changelog.md
42
Changelog.md
|
|
@ -64,3 +64,45 @@ Adds libinput configuration and virtual keyboard and pointer support.
|
||||||
- Add new cagebreak email contact gpg key
|
- Add new cagebreak email contact gpg key
|
||||||
- Add new project-repo AUR gpg key (relevant for cagebreak-pkgbuild)
|
- Add new project-repo AUR gpg key (relevant for cagebreak-pkgbuild)
|
||||||
|
|
||||||
|
## Release 2.0.0
|
||||||
|
|
||||||
|
This is a major release and includes BREAKING changes!
|
||||||
|
|
||||||
|
Breaking Changes:
|
||||||
|
* More intuitive switch of focus, next, prev and exchange (#20, 40 in Bugs.md)
|
||||||
|
Cagebreak will probably still feel broadly the same though.
|
||||||
|
* Remove -r flag - This is replaced by output configuration.
|
||||||
|
* Socket disabled by default - The socket is enabled by invoking Cagebreak with the -e flag.
|
||||||
|
* Socket permissions restricted to the user of the Cagebreak process (700)
|
||||||
|
This may require modification of scripts interacting with the socket with
|
||||||
|
different UIDs than the UID of the Cagebreak process.
|
||||||
|
* Rename "Current frame" indicator to "Current tile"
|
||||||
|
|
||||||
|
Changelog:
|
||||||
|
* Move to wlr_scene
|
||||||
|
* Add events displaying change over the socket
|
||||||
|
* this enables scripting tools
|
||||||
|
* Add support for scaling outputs (thanks to Oliver Friedmann)
|
||||||
|
* Remove -r flag
|
||||||
|
* Add rotation to output configuration
|
||||||
|
* Add cursor enable|disable flag
|
||||||
|
* Add input keyboard configuration
|
||||||
|
* Custom path flag for configuration file
|
||||||
|
* Restrict socket permissions to the user running cagebreak (700)
|
||||||
|
* Fix Issue #31 (resolve some terminology) 38 in Bugs.md
|
||||||
|
* Fix Issue #30 - 39 in Bugs.md
|
||||||
|
* Fix Issue #20 - 40 in Bugs.md
|
||||||
|
* Fix Issue #12 - 41 in Bugs.md (dump + events over socket)
|
||||||
|
* Fix Bugs found via scan-build static analysis (42 - 45 in Bugs.md)
|
||||||
|
* Fix Issue #33 - 46 in Bugs.md
|
||||||
|
* Fix Issue #32 - 47 in Bugs.md
|
||||||
|
* Fix Issue #16 - 48 in Bugs.md
|
||||||
|
* Fix Issue #3 - 49 in Bugs.md (disabling keybinding interpretation for specific keyboards is now possible)
|
||||||
|
* Fix Issue #26 - 50 in Bugs.md
|
||||||
|
* Fix Issue #7 - 51 in Bugs.md (Change cursor to square while Cagebreak is waiting for a key)
|
||||||
|
* Fix config bug for some commands - 52 in Bugs.md
|
||||||
|
* Fix Issue #35 - 53 in Bugs.md (update to wlroots 0.16.1)
|
||||||
|
* Improve FAQ.md (related to some Issues)
|
||||||
|
* Disable outputs when unable to set any mode (crashed previously)
|
||||||
|
* Add Code of Conduct
|
||||||
|
* Print version number on startup
|
||||||
|
|
|
||||||
60
FAQ.md
60
FAQ.md
|
|
@ -5,6 +5,7 @@
|
||||||
* Check the man pages
|
* Check the man pages
|
||||||
* [cagebreak config](man/cagebreak-config.5.md) (probably where you find the answer)
|
* [cagebreak config](man/cagebreak-config.5.md) (probably where you find the answer)
|
||||||
* [cagebreak](man/cagebreak.1.md) (command line options etc.)
|
* [cagebreak](man/cagebreak.1.md) (command line options etc.)
|
||||||
|
* [cagebreak-socket](man/cagebreak-socket.7.md) (socket information, mostly useful for scritping)
|
||||||
* Check the rest of this FAQ
|
* Check the rest of this FAQ
|
||||||
* [Open an issue](https://github.com/project-repo/cagebreak/issues/new) or otherwise get in touch with the development team (See section Email Contact in [SECURITY.md](SECURITY.md)).
|
* [Open an issue](https://github.com/project-repo/cagebreak/issues/new) or otherwise get in touch with the development team (See section Email Contact in [SECURITY.md](SECURITY.md)).
|
||||||
|
|
||||||
|
|
@ -25,6 +26,65 @@ Set the following environment variables
|
||||||
|
|
||||||
to activate wayland support for firefox.
|
to activate wayland support for firefox.
|
||||||
|
|
||||||
|
## How do I map characters which are not on the Keyboard?
|
||||||
|
|
||||||
|
Suppose you want to use a command like `bind` for a character
|
||||||
|
which is likely not on your keyboard like "ы" - you should write
|
||||||
|
it as "Cyrillic_yeru".
|
||||||
|
|
||||||
|
These names can be obtained by running xev and pressing the respective
|
||||||
|
key(s) (combinations).
|
||||||
|
|
||||||
|
## How do I change the size of the cursor?
|
||||||
|
|
||||||
|
The size and shape of the cursor is determined by the application, however,
|
||||||
|
most applications respect the environment variables `XCURSOR_SIZE` and
|
||||||
|
`XCURSOR_THEME`
|
||||||
|
|
||||||
|
To change the cursor size, one could use:
|
||||||
|
|
||||||
|
```
|
||||||
|
export XCURSOR_SIZE=1
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using an application which inherits its environment variables
|
||||||
|
from something outside the scope of the export (such as a daemon for
|
||||||
|
emacs might), you can usually specify your environment variables in
|
||||||
|
the applications respective configuration.
|
||||||
|
|
||||||
|
## How is Cagebreak launched?
|
||||||
|
|
||||||
|
The documentation says to start Cagebreak like any other binary.
|
||||||
|
|
||||||
|
If you want to start Cagebreak on login, you can use your shells
|
||||||
|
equivalent of a `.bash_profile`. In BASH appending cagebreak
|
||||||
|
(given that it is installed in the path) to `.bash_profile`
|
||||||
|
is fine.
|
||||||
|
|
||||||
|
If you want to start Cagebreak using systemd you could use a
|
||||||
|
service started on user login, though the development team once ran
|
||||||
|
into some issues with permissions for the services.
|
||||||
|
|
||||||
|
Please note that Cagebreak does not solve the problem of
|
||||||
|
locking the screen or login in general.
|
||||||
|
|
||||||
|
## How are applications launched in Cagebreak?
|
||||||
|
|
||||||
|
There are multiple ways to launch external programs in cagebreak.
|
||||||
|
|
||||||
|
* Using a terminal emulator is a perfectly valid way to launch
|
||||||
|
applications. If you want to be able to close the terminal
|
||||||
|
you used to start the program, you may use `&disown`, which should
|
||||||
|
be available in every shell.
|
||||||
|
* Using the `exec` command in the cagebreak config, it is possible to
|
||||||
|
launch programs when cagebreak is started. Moreover, as this command
|
||||||
|
can be combined with commands such as `bind`, it can be used to
|
||||||
|
create custom keybindings which execute external programs. (See also
|
||||||
|
the man page `cagebreak-config`).
|
||||||
|
* All commands mentioned previously are also available over the socket
|
||||||
|
which allows the creation of (arbitrary) keybindings on the fly and
|
||||||
|
a fortiori keybindings running arbitrary commands.
|
||||||
|
|
||||||
## Which further resources are available?
|
## Which further resources are available?
|
||||||
|
|
||||||
* [Arch Linux Wiki entry for Cagebreak](https://wiki.archlinux.org/title/Cagebreak)
|
* [Arch Linux Wiki entry for Cagebreak](https://wiki.archlinux.org/title/Cagebreak)
|
||||||
|
|
|
||||||
20
Hashes.md
20
Hashes.md
|
|
@ -1,5 +1,25 @@
|
||||||
# Hashes
|
# Hashes
|
||||||
|
|
||||||
|
2.0.0 cagebreak
|
||||||
|
|
||||||
|
* sha 256: 45f88abdae03bbc7dad5734dd6fb1a00e1bf37d3e0c4016dad2fa3de832335cf
|
||||||
|
* sha 512: dc261a788ea1843e701f4fb6985200c4813113fda0f911bee4e3cc9b20fd082055e40e9fc60647128674a94ece75f4aa55d1faede8e94e6e1940b02d8a6de8e0
|
||||||
|
|
||||||
|
2.0.0 cagebreak.1
|
||||||
|
|
||||||
|
* sha 256: 0d96947c2054fb42b284b31cc4ea793944a4562509f5f00b7927d11d0046041d
|
||||||
|
* sha 512: 4f3eadc88680d994f9ebcf85be38626b7e95971bc3233d710613cee58496553e0bb1e65258956f8a50c633bdd4dbc5c0283826fd5b661e9de0092366cc47caae
|
||||||
|
|
||||||
|
2.0.0 cagebreak-config.5
|
||||||
|
|
||||||
|
* sha 256: 4ee6ed443ce93cd6af3df49fe5e32244baeca04ca167993fb3d71970bf20939d
|
||||||
|
* sha 512: d6bf8f73ed8c9084b784653e88e1f149fbab35997f412f6c505972d3299a47bec1238aecd4334dd597bb32e46ced93f36d488badd25c6a8d61bdb73f3aea87ab
|
||||||
|
|
||||||
|
2.0.0 cagebreak-socket.7
|
||||||
|
|
||||||
|
* sha 256: 682b3cc46ad059147b7d7ab814ac1831689511e31c0d6f2b91732e85244da99e
|
||||||
|
* sha 512: 92057e4cbbcec4f4dbd98f3a99cb4c450ef31aef557705e5f8f2927b53653149b232771b455f9b9ae8435a7351667a4e663b7d120b76a0bbb5c7ba04db551489
|
||||||
|
|
||||||
1.9.1 cagebreak
|
1.9.1 cagebreak
|
||||||
|
|
||||||
* sha 256: 262c67170eb2a39174d40f97d940707029dca0b2b613b514a1cca29152a12665
|
* sha 256: 262c67170eb2a39174d40f97d940707029dca0b2b613b514a1cca29152a12665
|
||||||
|
|
|
||||||
586
README.md
586
README.md
|
|
@ -1,29 +1,81 @@
|
||||||
# Cagebreak: A Wayland Tiling Compositor Inspired by Ratpoison
|
# Cagebreak: A Wayland Tiling Compositor
|
||||||
|
|
||||||
This is Cagebreak, a Wayland tiling compositor.
|
[](https://bestpractices.coreinfrastructure.org/projects/6532) [](https://repology.org/project/cagebreak/versions) [](https://repology.org/project/cagebreak/versions)
|
||||||
|
|
||||||
The goal of this project is to provide a successor to ratpoison for Wayland
|
## Quick Introduction
|
||||||
users. However, this is no reimplementation of ratpoison.
|
|
||||||
|
|
||||||
Should you like to know if a feature will be implemented, open an issue
|
Cagebreak is a [Wayland](https://wayland.freedesktop.org/) tiling compositor
|
||||||
or get in touch with the development team.
|
based on [Cage](https://github.com/Hjdskes/cage) and inspired by [ratpoison](https://www.nongnu.org/ratpoison/).
|
||||||
|
|
||||||
For documentation of Cagebreak, please see
|
### Purpose
|
||||||
* the man pages
|
|
||||||
|
The goal of this project is to provide a successor to ratpoison for Wayland.
|
||||||
|
However, this is no reimplementation of ratpoison.
|
||||||
|
|
||||||
|
#### New Features, Bugs and Contact Information
|
||||||
|
|
||||||
|
Should you want to know if a feature will be implemented, file a bug or
|
||||||
|
get in touch, [open an issue](https://github.com/project-repo/cagebreak/issues/new)
|
||||||
|
or write an e-mail (See [SECURITY.md](SECURITY.md) for details.).
|
||||||
|
|
||||||
|
The Roadmap section outlines what is planned for the future.
|
||||||
|
|
||||||
|
#### Compatibility & Development Distribution
|
||||||
|
|
||||||
|
Cagebreak supports [Arch Linux](https://archlinux.org/) and uses the libraries
|
||||||
|
(and software versions) as they are obtained through [pacman](https://wiki.archlinux.org/title/Pacman)
|
||||||
|
at the time of release. Any other use is out of scope.
|
||||||
|
|
||||||
|
Most other setups probably work with a bit of luck. We
|
||||||
|
make no guarantees.
|
||||||
|
|
||||||
|
### Quick Installation
|
||||||
|
|
||||||
|
This assumes Arch Linux:
|
||||||
|
|
||||||
|
1. Use the [cagebreak PKGBUILD](https://aur.archlinux.org/packages/cagebreak).
|
||||||
|
2. Add an example config such as [config](examples/config) to `$USER/.config/cagebreak/config`
|
||||||
|
3. Execute cagebreak like any other binary.
|
||||||
|
|
||||||
|
See the [ArchWiki](https://wiki.archlinux.org/title/Cagebreak#Getting_started) for
|
||||||
|
details on getting started and the documentation for everything else.
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
* the rest of this file
|
||||||
|
* the man pages:
|
||||||
* [cagebreak](man/cagebreak.1.md)
|
* [cagebreak](man/cagebreak.1.md)
|
||||||
* [configuration](man/cagebreak-config.5.md)
|
* [configuration](man/cagebreak-config.5.md)
|
||||||
* also the [FAQ](FAQ.md)
|
* [socket](man/cagebreak-socket.7.md)
|
||||||
|
* the [FAQ](FAQ.md)
|
||||||
|
* [SECURITY.md](SECURITY.md)
|
||||||
|
|
||||||
|
#### What's new?
|
||||||
|
|
||||||
|
See the [Changelog](Changelog.md).
|
||||||
|
|
||||||
|
### Uninstallation
|
||||||
|
|
||||||
|
`pacman -R cagebreak` should be sufficient.
|
||||||
|
|
||||||
|
### Contributing
|
||||||
|
|
||||||
|
* Just [open an issue](https://github.com/project-repo/cagebreak/issues/new) and state your idea.
|
||||||
|
We will consider the proposal and get back to you.
|
||||||
|
* Don't open a pull request. We might not accept your code and
|
||||||
|
it would be sad to waste the effort.
|
||||||
|
* Respect the [Code of Conduct](CODE_OF_CONDUCT.md) (To date, we never
|
||||||
|
had to intervene - Keep it that way!)
|
||||||
|
|
||||||
|
### Name
|
||||||
|
|
||||||
Cagebreak is based on [Cage](https://github.com/Hjdskes/cage), a Wayland kiosk
|
Cagebreak is based on [Cage](https://github.com/Hjdskes/cage), a Wayland kiosk
|
||||||
compositor.
|
compositor. Since it breaks the kiosk into tiles the name
|
||||||
|
Cagebreak seemed appropriate.
|
||||||
Cagebreak is developed under Arch Linux and uses the libraries
|
|
||||||
as they are obtained through pacman. However, cagebreak should also work on
|
|
||||||
other distributions given the proper library versions.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
If you are using archlinux, just use the PKGBUILDs from the aur:
|
On Arch Linux, just use the PKGBUILDs from the [AUR](https://aur.archlinux.org/):
|
||||||
|
|
||||||
* Using [cagebreak](https://aur.archlinux.org/packages/cagebreak), Cagebreak is
|
* Using [cagebreak](https://aur.archlinux.org/packages/cagebreak), Cagebreak is
|
||||||
compiled on the target system (since release 1.3.0)
|
compiled on the target system (since release 1.3.0)
|
||||||
|
|
@ -37,17 +89,17 @@ See [cagebreak-pkgbuild](https://github.com/project-repo/cagebreak-pkgbuild) for
|
||||||
|
|
||||||
There are different ways to obtain cagebreak source:
|
There are different ways to obtain cagebreak source:
|
||||||
|
|
||||||
* git clone (for all releases)
|
* [git clone](https://github.com/project-repo/cagebreak) (for all releases)
|
||||||
* download release asset tarballs (starting at release 1.2.1)
|
* [download release asset tarballs](https://github.com/project-repo/cagebreak/releases) (starting at release 1.2.1)
|
||||||
|
|
||||||
### Verifying Source Code
|
#### Verifying Source Code
|
||||||
|
|
||||||
There are corresponding methods of verifying that you obtained the correct code:
|
There are corresponding methods of verifying that you obtained the correct code:
|
||||||
|
|
||||||
* our git history includes signed tags for releases
|
* our git history includes signed tags for releases
|
||||||
* release assets starting at release 1.2.1 contain a signature for the tarball
|
* release assets starting at release 1.2.1 contain a signature for the tarball
|
||||||
|
|
||||||
### Building Cagebreak
|
## Building Cagebreak
|
||||||
|
|
||||||
You can build Cagebreak with the [meson](https://mesonbuild.com/) build system. It
|
You can build Cagebreak with the [meson](https://mesonbuild.com/) build system. It
|
||||||
requires wayland, wlroots and xkbcommon to be installed. Note that Cagebreak is
|
requires wayland, wlroots and xkbcommon to be installed. Note that Cagebreak is
|
||||||
|
|
@ -57,14 +109,14 @@ breaking changes as soon as they occur.
|
||||||
Simply execute the following steps to build Cagebreak:
|
Simply execute the following steps to build Cagebreak:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ meson build
|
$ meson setup build
|
||||||
$ ninja -C build
|
$ ninja -C build
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Release Build
|
#### Release Build
|
||||||
|
|
||||||
By default, this builds a debug build. To build a release build, use `meson
|
By default, this builds a debug build. To build a release build, use `meson
|
||||||
build --buildtype=release`.
|
setup build --buildtype=release`.
|
||||||
|
|
||||||
##### Xwayland Support
|
##### Xwayland Support
|
||||||
|
|
||||||
|
|
@ -84,178 +136,129 @@ installed. Then, add `-Dman-pages=true` to the `meson` command.
|
||||||
You can start Cagebreak by running `./build/cagebreak`. If you run it from
|
You can start Cagebreak by running `./build/cagebreak`. If you run it from
|
||||||
within an existing X11 or Wayland session, it will open in a virtual output as
|
within an existing X11 or Wayland session, it will open in a virtual output as
|
||||||
a window in your existing session. If you run it in a TTY, it'll run with the
|
a window in your existing session. If you run it in a TTY, it'll run with the
|
||||||
KMS+DRM backend. For more configuration options, see the man pages.
|
KMS+DRM backend. Note that a configuration file is required. For more
|
||||||
|
configuration options, see the man pages.
|
||||||
|
|
||||||
## Contributing to Cagebreak
|
Please see `example_scripts/` for example scripts and a basis to customize
|
||||||
|
from.
|
||||||
|
|
||||||
Cagebreak is currently developed to fit the needs of its creators. Should you
|
#### Usage Philosophy
|
||||||
desire to implement a feature, please let us know in advance by opening
|
|
||||||
an issue. However, the feature set is intentionally limited (i.e. we removed
|
|
||||||
support for a desktop background) and will continue to be so in the future.
|
|
||||||
|
|
||||||
Nonetheless, don't be intimidated by the (slightly lengthy) release checklist
|
Cagebreak was originally built to suit the needs of its creators. This section outlines
|
||||||
or any other part of this file. Do what you can, open an issue and we will
|
how we intended some parts of cagebreak and might ease learning how to use cagebreak a
|
||||||
collaborate toward a solution.
|
little bit. Please note that this does not replace the man pages or the FAQ.
|
||||||
|
Also, this is in no way intended as a guide on how cagebreak must be used but rather
|
||||||
|
as a source of inspiration and of explanations for why certain particularities.
|
||||||
|
|
||||||
### Branching Strategy and Versioning
|
1. Cagebreak is keyboard-based. Everything regarding cagebreak can be done
|
||||||
|
through the keyboard and it is our view that it should be. This does not mean
|
||||||
|
that pointers, touchpads and such are not available for the few applications
|
||||||
|
that do require them.
|
||||||
|
|
||||||
There exists a branch `development` to which all reasonable code
|
2. Cagebreak is a tiling compositor. Every view takes up as much screen space
|
||||||
is comitted for final testing.
|
as possible. We believe this is useful, as only very few programs are typically
|
||||||
|
necessary to complete a task. To manage multiple tasks concurrently, we use
|
||||||
|
workspaces.
|
||||||
|
|
||||||
Once `development` is ready for a release, it is merged into `master` (possibly via
|
3. Each task deserves its own workspace. Any given task (the sort of thing you
|
||||||
a cherry-picked branch), creating a new release, which is tagged and signed.
|
might find in your calendar or on your todo list) probably requires very few
|
||||||
|
views and ideally, these take up as much of the screen as possible.
|
||||||
|
|
||||||
All releases are tagged according to [semantic versioning](https://semver.org) guidelines.
|
> Combining 2. and 3. might look like this in practice:
|
||||||
|
|
||||||
In the past, our git history did not perfectly reflect this scheme.
|
> * Task 1: Edit introduction section for paper on X
|
||||||
|
> * Task 2: Coordinate event with person Y
|
||||||
|
|
||||||
### Releases
|
> * split screen vertically
|
||||||
|
> * open web browser or pdf viewer to read literature
|
||||||
|
> * focus next
|
||||||
|
> * open editor
|
||||||
|
> * change to a different workspace
|
||||||
|
> * split screen vertically
|
||||||
|
> * open calendar application
|
||||||
|
> * focus next
|
||||||
|
> * open chat application
|
||||||
|
|
||||||
The release checklist must be completely fulfilled in one run for a release to
|
> Now each task has its own workspace and switching between tasks is possible
|
||||||
occur.
|
> by switching between workspaces.
|
||||||
|
|
||||||
* [ ] `git checkout development`
|
> Note that, for example by using the socket, more advanced setups are
|
||||||
* [ ] `git pull origin development`
|
> possible. But the user is warned that excessive tweaking eats into the work
|
||||||
* [ ] `git push origin development`
|
> to be done.
|
||||||
* [ ] `ninja -C build clang-format` makes no changes
|
|
||||||
* [ ] New version number determined according to [semantic versioning](https://semver.org) guidelines
|
|
||||||
* [ ] Relevant Documentation completed
|
|
||||||
* [ ] New features
|
|
||||||
* [ ] man pages
|
|
||||||
* [ ] man/cagebreak
|
|
||||||
* [ ] man/cagebreak-config
|
|
||||||
* [ ] Set EPOCH to release day in man generation in meson.build
|
|
||||||
* [ ] FAQ.md
|
|
||||||
* [ ] Changelog.md for major and minor releases but not patches
|
|
||||||
* [ ] Check features for SECURITY.md relevance (changes to socket scope
|
|
||||||
for example)
|
|
||||||
* [ ] Fixed bugs documented in Bugs.md
|
|
||||||
* [ ] Include issue description from github
|
|
||||||
* [ ] Testing
|
|
||||||
* [ ] Manual testing
|
|
||||||
* [ ] Libfuzzer testing
|
|
||||||
* [ ] Build version without xwayland support
|
|
||||||
* [ ] Version Number
|
|
||||||
* [ ] meson.build
|
|
||||||
* [ ] git tag
|
|
||||||
* [ ] man pages
|
|
||||||
* [ ] meson.build reproducible build versions are current archlinux libraries and gcc
|
|
||||||
* [ ] Cagebreak is reproducible on multiple machines
|
|
||||||
* [ ] Documented reproducible build artefacts
|
|
||||||
* [ ] Hashes of the artefacts in README.md
|
|
||||||
* [ ] Renamed previous signatures
|
|
||||||
* [ ] Created gpg signature of the artefacts
|
|
||||||
* [ ] `gpg --detach-sign -u keyid cagebreak`
|
|
||||||
* [ ] `gpg --detach-sign -u keyid cagebreak.1`
|
|
||||||
* [ ] `gpg --detach-sign -u keyid cagebreak-config.5`
|
|
||||||
* [ ] `git add` relevant files
|
|
||||||
* [ ] `git commit`
|
|
||||||
* [ ] `git push origin development`
|
|
||||||
* [ ] Determined commit and tag message (Start with "Release version_number\n\n")
|
|
||||||
* [ ] Mentioned fixed Bugs.md issues ("Fixed Issue n")
|
|
||||||
* [ ] Mentioned other important changes
|
|
||||||
* [ ] `git checkout master`
|
|
||||||
* [ ] `git merge --squash development`
|
|
||||||
* [ ] `git commit` and insert message
|
|
||||||
* [ ] `git tag -u keyid version HEAD` and insert message
|
|
||||||
* [ ] `git tag -v version` and check output
|
|
||||||
* [ ] `git push --tags origin master`
|
|
||||||
* [ ] `git checkout development`
|
|
||||||
* [ ] `git merge master`
|
|
||||||
* [ ] `git push --tags origin development`
|
|
||||||
* [ ] `git archive --prefix=cagebreak/ -o release_version.tar.gz tags/version .`
|
|
||||||
* [ ] Create release-artefacts_version.tar.gz
|
|
||||||
* [ ] `mkdir release-artefacts_version`
|
|
||||||
* [ ] `cp build/cagebreak release-artefacts_version/`
|
|
||||||
* [ ] `cp build/cagebreak.sig release-artefacts_version/`
|
|
||||||
* [ ] `cp build/cagebreak.1 release-artefacts_version/`
|
|
||||||
* [ ] `cp build/cagebreak.1.sig release-artefacts_version/`
|
|
||||||
* [ ] `cp build/cagebreak-config.5 release-artefacts_version/`
|
|
||||||
* [ ] `cp build/cagebreak-config.5.sig release-artefacts_version/`
|
|
||||||
* [ ] `cp LICENSE release-artefacts_version/`
|
|
||||||
* [ ] `cp README.md release-artefacts_version/`
|
|
||||||
* [ ] `cp SECURITY.md release-artefacts_version/`
|
|
||||||
* [ ] `cp FAQ.md release-artefacts_version/`
|
|
||||||
* [ ] `export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) ; tar --sort=name --mtime= --owner=0 --group=0 --numeric-owner -czf release-artefacts_version.tar.gz release-artefacts_version`
|
|
||||||
* [ ] Checked archive
|
|
||||||
* [ ] tar -xvf release_version.tar.gz
|
|
||||||
* [ ] cd cagebreak
|
|
||||||
* [ ] meson build --buildtype=release
|
|
||||||
* [ ] ninja -C build
|
|
||||||
* [ ] gpg --verify ../signatures/cagebreak.sig build/cagebreak
|
|
||||||
* [ ] cd ..
|
|
||||||
* [ ] rm -rf cagebreak
|
|
||||||
* [ ] `gpg --detach-sign -u keyid release_version.tar.gz`
|
|
||||||
* [ ] `gpg --detach-sign -u keyid release-artefacts_version.tar.gz`
|
|
||||||
* [ ] Upload archives and signatures as release assets
|
|
||||||
|
|
||||||
### Reproducible Builds
|
4. Use keybindings and terminal emulators for the right purpose. Given the
|
||||||
|
philosophy outlined above you probably launch the same few programs very
|
||||||
|
often and others are very rarely used. We believe that commonly
|
||||||
|
used programs should have their own keybindings together with the most
|
||||||
|
important cagebreak commands. All the rarely used programs should be launched
|
||||||
|
from a terminal emulator as they probably require special flags, environment
|
||||||
|
variables and file paths anyway.
|
||||||
|
|
||||||
Cagebreak offers reproducible builds given the exact library versions specified
|
> In practice this means thinking about the applications and cagebreak commands
|
||||||
in `meson.build`. Should a version mismatch occur, a warning will be emitted. We have
|
> you use and taking your keyboard layout into account when defining keybindings for
|
||||||
decided on this compromise to allow flexibility and security. In general we will
|
> your individual needs.
|
||||||
adapt the versions to the packages available under Arch Linux at the time of
|
|
||||||
release.
|
|
||||||
|
|
||||||
There are reproducibility issues up to and including release `1.2.0`. See
|
5. Cagebreak can't do everything, but with scripting you can do most things.
|
||||||
`Issue 5` in [Bugs.md](Bugs.md).
|
Through the socket and with a bit of scripting, you can use the internal state
|
||||||
|
of cagebreak in combination with cagebreak commands and the full power of
|
||||||
|
a scripting language of your choice to do almost whatever you want.
|
||||||
|
|
||||||
#### Reproducible Build Instructions
|
> Example scripts can be found in the repository under `example_scripts/`.
|
||||||
|
|
||||||
All hashes and signatures are provided for the following build instructions.
|
## Contributing
|
||||||
|
|
||||||
```
|
* Read this document.
|
||||||
meson build -Dxwayland=true -Dman-pages=true --buildtype=release
|
* Just [open an issue](https://github.com/project-repo/cagebreak/issues/new) and state your feature request.
|
||||||
ninja -C build
|
We will consider the proposal and get back to you.
|
||||||
```
|
* Don't open a pull request without asking first. We might not accept your
|
||||||
|
code and it would be sad to waste the effort.
|
||||||
|
* Respect the [Code of Conduct](CODE_OF_CONDUCT.md) (To date, we never
|
||||||
|
had to intervene - Please keep it that way!)
|
||||||
|
|
||||||
#### Hashes for Builds
|
### Good First Contributions
|
||||||
|
|
||||||
For every release after 1.0.5, hashes will be provided.
|
* Reviewing the project is always welcome.
|
||||||
|
* Read the code.
|
||||||
|
* Read the documentation.
|
||||||
|
* Test whether the documentation matches the code.
|
||||||
|
* Test Cagebreak in more esoteric setups (many monitors, for instance).
|
||||||
|
* Compile the code.
|
||||||
|
* Ideas on improving the testing and quality assurance are particularly
|
||||||
|
welcome.
|
||||||
|
* If you want, you can share your cagebreak scripts and we might include them
|
||||||
|
in the repository provided you agree to release them under MIT and we agree
|
||||||
|
with the use case and coding style.
|
||||||
|
* Iff you are happy with Cagebreak and use Arch Linux, you may vote for
|
||||||
|
[Cagebreak in the AUR](https://aur.archlinux.org/packages/cagebreak).
|
||||||
|
* The points above still apply.
|
||||||
|
|
||||||
For every release after 1.7.0, hashes will be provided for man pages too.
|
### Philosophy
|
||||||
|
|
||||||
See [Hashes.md](Hashes.md)
|
Cagebreak is currently developed to fit the needs of its creators.
|
||||||
|
|
||||||
#### GPG Signatures
|
The feature set is intentionally limited - we removed
|
||||||
|
support for a desktop background image for example.
|
||||||
|
|
||||||
For every release after 1.0.5, a GPG signature will be provided in `signatures`.
|
Nonetheless, don't be intimidated by any other part of this file.
|
||||||
|
Do your best and we will collaborate toward a solution.
|
||||||
|
|
||||||
The current signature is called `cagebreak.sig`, whereas all older signatures
|
## Development
|
||||||
will be named after their release version.
|
|
||||||
|
|
||||||
Due to errors in the release process, the releases 1.7.1 and 1.7.2 did not include the release
|
### Compatibility & Development Distribution
|
||||||
signatures in the appropriate folder of the git repository. However, signatures were provided
|
|
||||||
as release-artefacts at the time of release. The signatures were introduced into the
|
|
||||||
repository with 1.7.3. The integrity of cagebreak is still the same because the signatures were
|
|
||||||
provided as release-artefacts (which were themselves signed) and the hashes in README.md
|
|
||||||
are part of a signed release tag.
|
|
||||||
|
|
||||||
#### Signing Keys
|
Cagebreak supports [Arch Linux](https://archlinux.org/) and uses the libraries
|
||||||
|
(and software versions) as they are obtained through [pacman](https://wiki.archlinux.org/title/Pacman)
|
||||||
|
at the time of release. Any other use is out of scope.
|
||||||
|
|
||||||
All releases are signed by at least one of the following collection of
|
However, Cagebreak may also work on other distributions given the
|
||||||
keys.
|
proper library versions (Some package maintainers have done this and it
|
||||||
|
seems to work (To date, we dealt with a few Issues and never felt the
|
||||||
|
need to ask for the distribution the user was having the issue on.)).
|
||||||
|
|
||||||
* E79F6D9E113529F4B1FFE4D5C4F974D70CEC2C5B
|
[](https://repology.org/project/cagebreak/versions)
|
||||||
* 4739D329C9187A1C2795C20A02ABFDEC3A40545F
|
|
||||||
* 7535AB89220A5C15A728B75F74104CC7DCA5D7A8
|
|
||||||
* 827BC2320D535AEAD0540E6E2E66F65D99761A6F
|
|
||||||
* A88D7431E5BAAD0B6EAE550AC8D61D8BD4FA3C46
|
|
||||||
* 8F872885968EB8C589A32E9539ACC012896D450F
|
|
||||||
* 896B92AF738C974E0065BF42F2576BD366156BB9
|
|
||||||
* AA927AFD50AF7C6810E69FE8274F2C605359E31B
|
|
||||||
* BE2DED372287BC4EB2213E13A0C743848A638955
|
|
||||||
* 0F3476E4B2404F95EC41600683D5810F7911B020
|
|
||||||
|
|
||||||
Should we at any point retire a key, we will only replace it with keys signed
|
You should use Arch Linux if you want to modify Cagebreak
|
||||||
by at least one of the above collection.
|
for yourself.
|
||||||
|
|
||||||
We registered project-repo.co and added mail addresses after release `1.3.0`.
|
|
||||||
|
|
||||||
We now have a mail address and its key is signed by signing keys. See Security
|
|
||||||
Bugs for details.
|
|
||||||
|
|
||||||
The full public keys can be found in `keys/` along with any revocation certificates.
|
|
||||||
|
|
||||||
### GCC and -fanalyzer
|
### GCC and -fanalyzer
|
||||||
|
|
||||||
|
|
@ -298,7 +301,7 @@ Here is an example workflow:
|
||||||
|
|
||||||
```
|
```
|
||||||
rm -rf build
|
rm -rf build
|
||||||
CC=clang meson build -Dfuzz=true -Db_sanitize=address,undefined -Db_lundef=false
|
CC=clang meson setup build -Dfuzz=true -Db_sanitize=address,undefined -Db_lundef=false
|
||||||
ninja -C build/
|
ninja -C build/
|
||||||
mkdir build/fuzz_corpus
|
mkdir build/fuzz_corpus
|
||||||
cp examples/config build/fuzz_corpus/
|
cp examples/config build/fuzz_corpus/
|
||||||
|
|
@ -316,35 +319,254 @@ Currently, there are memory leaks which do not seem to stem from our code but ra
|
||||||
the code of wl-roots or some other library we depend on. We are working on the problem.
|
the code of wl-roots or some other library we depend on. We are working on the problem.
|
||||||
In the meantime, add `-Db_detect-leaks=0` to the meson command to exclude memory leaks.
|
In the meantime, add `-Db_detect-leaks=0` to the meson command to exclude memory leaks.
|
||||||
|
|
||||||
|
### Reproducible Builds
|
||||||
|
|
||||||
|
Cagebreak offers reproducible builds given the exact library versions specified
|
||||||
|
in `meson.build`. Should a version mismatch occur, a warning will be emitted. We have
|
||||||
|
decided on this compromise to allow flexibility and security. In general we will
|
||||||
|
adapt the versions to the packages available under Arch Linux at the time of
|
||||||
|
release.
|
||||||
|
|
||||||
|
There are reproducibility issues up to and including release `1.2.0`. See
|
||||||
|
`Issue 5` in [Bugs.md](Bugs.md).
|
||||||
|
|
||||||
|
#### Reproducible Build Instructions
|
||||||
|
|
||||||
|
All hashes and signatures are provided for the following build instructions.
|
||||||
|
|
||||||
|
```
|
||||||
|
meson setup build -Dxwayland=true -Dman-pages=true --buildtype=release
|
||||||
|
ninja -C build
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Hashes for Builds
|
||||||
|
|
||||||
|
For every release after 1.0.5, hashes will be provided.
|
||||||
|
|
||||||
|
For every release after 1.7.0, hashes will be provided for man pages too.
|
||||||
|
|
||||||
|
See [Hashes.md](Hashes.md)
|
||||||
|
|
||||||
|
#### GPG Signatures
|
||||||
|
|
||||||
|
For every release after 1.0.5, a GPG signature will be provided in `signatures`.
|
||||||
|
|
||||||
|
The current signature is called `cagebreak.sig`, whereas all older signatures
|
||||||
|
will be named after their release version.
|
||||||
|
|
||||||
|
Due to errors in the release process, the releases 1.7.1 and 1.7.2 did not include the release
|
||||||
|
signatures in the appropriate folder of the git repository. However, signatures were provided
|
||||||
|
as release-artefacts at the time of release. The signatures were introduced into the
|
||||||
|
repository with 1.7.3. The integrity of cagebreak is still the same because the signatures were
|
||||||
|
provided as release-artefacts (which were themselves signed) and the hashes in Hashes.md
|
||||||
|
are part of a signed release tag.
|
||||||
|
|
||||||
|
#### Signing Keys
|
||||||
|
|
||||||
|
All releases are signed by at least one of the following collection of
|
||||||
|
keys.
|
||||||
|
|
||||||
|
* E79F6D9E113529F4B1FFE4D5C4F974D70CEC2C5B
|
||||||
|
* 4739D329C9187A1C2795C20A02ABFDEC3A40545F
|
||||||
|
* 7535AB89220A5C15A728B75F74104CC7DCA5D7A8
|
||||||
|
* 827BC2320D535AEAD0540E6E2E66F65D99761A6F
|
||||||
|
* A88D7431E5BAAD0B6EAE550AC8D61D8BD4FA3C46
|
||||||
|
* 8F872885968EB8C589A32E9539ACC012896D450F
|
||||||
|
* 896B92AF738C974E0065BF42F2576BD366156BB9
|
||||||
|
* AA927AFD50AF7C6810E69FE8274F2C605359E31B
|
||||||
|
* BE2DED372287BC4EB2213E13A0C743848A638955
|
||||||
|
* 0F3476E4B2404F95EC41600683D5810F7911B020
|
||||||
|
|
||||||
|
Should we at any point retire a key, we will only replace it with keys signed
|
||||||
|
by at least one of the above collection.
|
||||||
|
|
||||||
|
We registered project-repo.co and added mail addresses after release `1.3.0`.
|
||||||
|
|
||||||
|
We now have a mail address and its key is signed by signing keys. See Security
|
||||||
|
Bugs for details.
|
||||||
|
|
||||||
|
The full public keys can be found in `keys/` along with any revocation certificates.
|
||||||
|
|
||||||
|
### Versioning & Branching Strategy
|
||||||
|
|
||||||
|
Cagebreak uses [semantic versioning](https://semver.org).
|
||||||
|
|
||||||
|
There are three permanent branches in cagebreak:
|
||||||
|
|
||||||
|
* `master` (for releases)
|
||||||
|
* `development` (for polishing code between releases)
|
||||||
|
* `hotfix` (for small emergent releases, usually up-to-date with master)
|
||||||
|
|
||||||
|
Releases are merged to master as per the release procedure, with
|
||||||
|
reasonable exceptions as the situation requires.
|
||||||
|
|
||||||
|
The release commit is tagged with the release version.
|
||||||
|
|
||||||
|
In the past, our git history did not perfectly reflect this scheme.
|
||||||
|
|
||||||
|
### Release Procedure
|
||||||
|
|
||||||
|
The release procedure outlines the process for a release to occur.
|
||||||
|
|
||||||
|
* [ ] `git checkout development`
|
||||||
|
* [ ] `git pull origin development`
|
||||||
|
* [ ] `git push origin development`
|
||||||
|
* [ ] New semantic version number determined
|
||||||
|
* [ ] Adjust version number
|
||||||
|
* [ ] meson.build
|
||||||
|
* [ ] git tag
|
||||||
|
* [ ] man pages
|
||||||
|
* [ ] README.md repology badges minversion
|
||||||
|
* [ ] Relevant Documentation completed
|
||||||
|
* [ ] New features
|
||||||
|
* [ ] man pages
|
||||||
|
* [ ] cagebreak
|
||||||
|
* [ ] cagebreak-config
|
||||||
|
* [ ] cagebreak-socket
|
||||||
|
* [ ] example config
|
||||||
|
* [ ] Set EPOCH to release day in man generation in meson.build
|
||||||
|
* [ ] FAQ.md
|
||||||
|
* [ ] Changelog.md for major and minor releases but not patches
|
||||||
|
* [ ] Check features for SECURITY.md relevance (changes to socket scope
|
||||||
|
for example)
|
||||||
|
* [ ] Synchronize any socket changes to cagebreak-socket man page
|
||||||
|
* [ ] Fixed bugs documented in Bugs.md
|
||||||
|
* [ ] Include issue discussion from github, where applicable
|
||||||
|
* [ ] Testing
|
||||||
|
* [ ] Manual testing
|
||||||
|
* [ ] Libfuzzer testing
|
||||||
|
* [ ] Build version without xwayland support
|
||||||
|
* [ ] meson.build reproducible build versions are current archlinux libraries and gcc
|
||||||
|
* [ ] wlr_xdg_shell version check
|
||||||
|
* [ ] `ninja -C build clang-format` makes no changes
|
||||||
|
* [ ] `ninja -C build scan-build` shows no issues
|
||||||
|
* [ ] Cagebreak is reproducible on multiple machines
|
||||||
|
* [ ] Documented reproducible build artefacts
|
||||||
|
* [ ] Hashes of the artefacts in Hashes.md
|
||||||
|
* [ ] Renamed previous signatures
|
||||||
|
* [ ] Created gpg signature of the artefacts
|
||||||
|
* [ ] `gpg --detach-sign -u keyid cagebreak`
|
||||||
|
* [ ] `gpg --detach-sign -u keyid cagebreak.1`
|
||||||
|
* [ ] `gpg --detach-sign -u keyid cagebreak-config.5`
|
||||||
|
* [ ] `gpg --detach-sign -u keyid cagebreak-socket.7`
|
||||||
|
* [ ] `git add` relevant files
|
||||||
|
* [ ] `git commit`
|
||||||
|
* [ ] `git push origin development`
|
||||||
|
* [ ] Determined commit and tag message (Start with "Release version_number\n\n")
|
||||||
|
* [ ] Mentioned fixed Bugs.md issues ("Fixed Issue n")
|
||||||
|
* [ ] Mentioned other important changes
|
||||||
|
* [ ] `git checkout master`
|
||||||
|
* [ ] `git merge --squash development`
|
||||||
|
* [ ] `git commit` and insert message
|
||||||
|
* [ ] `git tag -u keyid version HEAD` and insert message
|
||||||
|
* [ ] `git tag -v version` and check output
|
||||||
|
* [ ] `git push --tags origin master`
|
||||||
|
* [ ] `git checkout development` (merge to development depends on whether release was a hotfix)
|
||||||
|
* [ ] `git merge master`
|
||||||
|
* [ ] `git push --tags origin development`
|
||||||
|
* [ ] `git checkout hotfix` (hotfix is to be kept current with master after releases)
|
||||||
|
* [ ] `git merge master`
|
||||||
|
* [ ] `git push --tags origin hotfix`
|
||||||
|
* [ ] `git archive --prefix=cagebreak/ -o release_version.tar.gz tags/version .`
|
||||||
|
* [ ] Create release-artefacts_version.tar.gz
|
||||||
|
* [ ] `mkdir release-artefacts_version`
|
||||||
|
* [ ] `cp build/cagebreak release-artefacts_version/`
|
||||||
|
* [ ] `cp build/cagebreak.sig release-artefacts_version/`
|
||||||
|
* [ ] `cp build/cagebreak.1 release-artefacts_version/`
|
||||||
|
* [ ] `cp build/cagebreak.1.sig release-artefacts_version/`
|
||||||
|
* [ ] `cp build/cagebreak-config.5 release-artefacts_version/`
|
||||||
|
* [ ] `cp build/cagebreak-config.5.sig release-artefacts_version/`
|
||||||
|
* [ ] `cp build/cagebreak-socket.7 release-artefacts_version/`
|
||||||
|
* [ ] `cp build/cagebreak-socket.7.sig release-artefacts_version/`
|
||||||
|
* [ ] `cp LICENSE release-artefacts_version/`
|
||||||
|
* [ ] `cp README.md release-artefacts_version/`
|
||||||
|
* [ ] `cp SECURITY.md release-artefacts_version/`
|
||||||
|
* [ ] `cp FAQ.md release-artefacts_version/`
|
||||||
|
* [ ] `export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) ; tar --sort=name --mtime= --owner=0 --group=0 --numeric-owner -czf release-artefacts_version.tar.gz release-artefacts_version`
|
||||||
|
* [ ] Checked archive
|
||||||
|
* [ ] tar -xvf release_version.tar.gz
|
||||||
|
* [ ] cd cagebreak
|
||||||
|
* [ ] meson setup build -Dxwayland=true -Dman-pages=true --buildtype=release
|
||||||
|
* [ ] ninja -C build
|
||||||
|
* [ ] gpg --verify ../signatures/cagebreak.sig build/cagebreak
|
||||||
|
* [ ] cd ..
|
||||||
|
* [ ] rm -rf cagebreak
|
||||||
|
* [ ] `gpg --detach-sign -u keyid release_version.tar.gz`
|
||||||
|
* [ ] `gpg --detach-sign -u keyid release-artefacts_version.tar.gz`
|
||||||
|
* [ ] Upload archives and signatures as release assets
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
Cagebreak plans to do or keep doing the following things
|
||||||
|
in the future:
|
||||||
|
|
||||||
|
* React to all issues.
|
||||||
|
* Add or modify features, which the authors find convenient or important.
|
||||||
|
* Improve the [OpenSSF Best Practices Badge Program](https://bestpractices.coreinfrastructure.org/en) level
|
||||||
|
|
||||||
|
## Governance
|
||||||
|
|
||||||
|
Cagebreak is managed by project-repo.
|
||||||
|
|
||||||
|
Project-repo is a pseudonym of at least two individuals acting
|
||||||
|
as benevolent dictators for the project by the others mutual consent.
|
||||||
|
|
||||||
|
The individuals comprising project-repo are not otherwise associated
|
||||||
|
by payment from any organisation or grant.
|
||||||
|
|
||||||
|
For all intents and purposes consider project-repo as a single
|
||||||
|
benevolent dictator for life that happens to occupy at least two brains.
|
||||||
|
|
||||||
|
### Roles
|
||||||
|
|
||||||
|
There are members of project-repo and those who are not.
|
||||||
|
|
||||||
|
There are no specific roles forced unto anyone.
|
||||||
|
|
||||||
|
### Bus Factor
|
||||||
|
|
||||||
|
The Bus Factor is a measure of how many people have to be
|
||||||
|
incapacitated for a project to be unable to continue.
|
||||||
|
|
||||||
|
The current bus factor for Cagebreak is: 1
|
||||||
|
|
||||||
|
Project-repo could still react to issues (even confidential
|
||||||
|
e-mails) and fix easier issues if any one individual were
|
||||||
|
incapacitated.
|
||||||
|
|
||||||
|
However, not all aspects of the code or release engineering
|
||||||
|
are fully resilient to the loss of any one individual.
|
||||||
|
|
||||||
|
We strive to improve the Bus Factor to at least two in all
|
||||||
|
aspects of Cagebreak.
|
||||||
|
|
||||||
|
### Governance Issues
|
||||||
|
|
||||||
|
Anyone can use the information in [SECURITY.md](SECURITY.md) to
|
||||||
|
contact the members of project-repo and bring governance
|
||||||
|
issues to their attention.
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
For any bug, please [create an
|
For any bug, please [create an issue](https://github.com/project-repo/cagebreak/issues/new) on
|
||||||
issue](https://github.com/project-repo/cagebreak/issues/new) on
|
[GitHub](https://github.com/project-repo/cagebreak).
|
||||||
[GitHub](https://github.com/project-rep/cagebreak).
|
|
||||||
|
|
||||||
Fixed bugs are to be assigned a number and summarized inside Bugs.md for future reference
|
Fixed bugs are to be assigned a number and summarized inside Bugs.md for future reference
|
||||||
independent of github, in case this service is unavailable.
|
independent of github, in case this service is unavailable.
|
||||||
|
|
||||||
Mail contact: `cagebreak @ project-repo . co`
|
For other means of contacting the Cagebreak authors and for security issues
|
||||||
|
see [SECURITY.md](SECURITY.md).
|
||||||
GPG Fingerprints:
|
|
||||||
|
|
||||||
* B15B92642760E11FE002DE168708D42451A94AB5
|
|
||||||
* F8DD9F8DD12B85A28F5827C4678E34D2E753AA3C
|
|
||||||
* 3ACEA46CCECD59E4C8222F791CBEB493681E8693
|
|
||||||
|
|
||||||
See [SECURITY.md](SECURITY.md) for details.
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
See [Changelog.md](Changelog.md)
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
* Aisha Tammy
|
* Aisha Tammy
|
||||||
* [make man pages optional](https://github.com/project-repo/cagebreak/pull/4), released
|
* [make man pages optional](https://github.com/project-repo/cagebreak/pull/4), released
|
||||||
in 1.6.0 with slight modifications
|
in 1.6.0 with slight modifications
|
||||||
|
* Oliver Friedmann
|
||||||
|
* [Add output scaling](https://github.com/project-repo/cagebreak/pull/34), released
|
||||||
|
in 2.0.0 with slight modifications
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Please see [LICENSE](https://github.com/project-repo/cagebreak/blob/master/LICENSE)
|
MIT, please see [LICENSE](https://github.com/project-repo/cagebreak/blob/master/LICENSE).
|
||||||
|
|
||||||
|
|
|
||||||
62
SECURITY.md
62
SECURITY.md
|
|
@ -1,10 +1,13 @@
|
||||||
# Security
|
# Security
|
||||||
|
|
||||||
The main possibility for security bugs to occur in cagebreak is by privilege
|
The main possibility for security bugs in cagebreak is by privilege
|
||||||
escalation using the socket. Indeed, any program with access to the socket
|
escalation through the socket. Any program with access to the socket
|
||||||
immediately gains arbitrary code execution rights. As of right now, the socket
|
immediately gains arbitrary code execution rights. The socket
|
||||||
is world-writable. If you disagree with this threat model, you may contact
|
is restricted to the user of the cagebreak process (700) and has to
|
||||||
us via email (See section Email Contact below.) or [open an issue on github](https://github.com/project-repo/cagebreak/issues/new).
|
be explicitely enabled using the `-e` flag on invocation.
|
||||||
|
|
||||||
|
If you disagree with this threat model, you may contact us via email (See
|
||||||
|
section Email Contact below.) or [open an issue on github](https://github.com/project-repo/cagebreak/issues/new).
|
||||||
|
|
||||||
Should any problem with the github issue system arise or any other reason
|
Should any problem with the github issue system arise or any other reason
|
||||||
for (potentially confidential) contact with the Cagebreak authors appear,
|
for (potentially confidential) contact with the Cagebreak authors appear,
|
||||||
|
|
@ -12,9 +15,9 @@ you may contact us via email (See section Email Contact below.).
|
||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
The latest release always contains the latest bug fixes and features.
|
The most recent release always contains the latest bug fixes and features.
|
||||||
There are no official backports for security vulnerabilities. Builds
|
There are no official backports for security vulnerabilities.
|
||||||
are reproducible under conditions outlined in [README.md](README.md).
|
Builds are reproducible under conditions outlined in [README.md](README.md).
|
||||||
|
|
||||||
## Bug Reports
|
## Bug Reports
|
||||||
|
|
||||||
|
|
@ -25,20 +28,20 @@ is available below.
|
||||||
|
|
||||||
## Email Contact
|
## Email Contact
|
||||||
|
|
||||||
Should you want to get in touch with the developers of cagebreak to report
|
If you want to get in touch with the developers of cagebreak to report
|
||||||
a security vulnerability or anything else via email, contact
|
a security vulnerability or anything else via email, contact
|
||||||
`cagebreak @ project-repo . co`.
|
`cagebreak @ project-repo . co`.
|
||||||
|
|
||||||
We will try to respond to everything that is not obvious spam.
|
We try to respond to everything that is not obvious spam.
|
||||||
|
|
||||||
### GPG-Encrypted Emails
|
### GPG-Encrypted Emails
|
||||||
|
|
||||||
If you can, please encrypt your email with the appropriate GPG key found
|
If you can, please encrypt your email with the appropriate GPG key found
|
||||||
in `keys/` and sign your message with your own key.
|
in `keys/` and sign your message with your own key.
|
||||||
|
|
||||||
* B15B92642760E11FE002DE168708D42451A94AB5
|
* B15B92642760E11FE002DE168708D42451A94AB5
|
||||||
* F8DD9F8DD12B85A28F5827C4678E34D2E753AA3C
|
* F8DD9F8DD12B85A28F5827C4678E34D2E753AA3C
|
||||||
* 3ACEA46CCECD59E4C8222F791CBEB493681E8693
|
* 3ACEA46CCECD59E4C8222F791CBEB493681E8693
|
||||||
|
|
||||||
Note that our keys are signed by cagebreak signing keys.
|
Note that our keys are signed by cagebreak signing keys.
|
||||||
|
|
||||||
|
|
@ -47,21 +50,23 @@ public key or provide the fingerprint and directions to obtain the key.
|
||||||
|
|
||||||
## Threat Model
|
## Threat Model
|
||||||
|
|
||||||
Cagebreak is a wayland compositor, which is run by the user of the system
|
Cagebreak is a wayland compositor run by the user of the system
|
||||||
and thus has access to whichever resources this user has access to.
|
and thus has access to whichever resources this user has access to.
|
||||||
Cagebreak can restrict other programs in no way, because this would hamper
|
Cagebreak can restrict other programs in no way, because this would hamper
|
||||||
usability (consider a web browser unable to write a downloaded file to disk
|
usability (consider a web browser unable to write a downloaded file to disk
|
||||||
for instance). There is no transmission of information by cagebreak other
|
for instance).
|
||||||
than to the screens, ipc and potentially other documented local channels.
|
|
||||||
|
There is no transmission of information by cagebreak other than to the
|
||||||
|
screens, ipc and potentially other documented local channels.
|
||||||
|
|
||||||
### STRIDE Threat List
|
### STRIDE Threat List
|
||||||
|
|
||||||
This is not a thorough analysis, just an overview of the ways in which cagebreak
|
This is not a thorough analysis, just an overview of the ways in which cagebreak
|
||||||
has/does not have an attack surface.
|
has (no) attack surface.
|
||||||
|
|
||||||
#### Spoofing
|
#### Spoofing
|
||||||
|
|
||||||
Not applicable - Using cagebreak already requires a login as a user.
|
Not applicable - Using Cagebreak already requires a login as a user.
|
||||||
|
|
||||||
#### Tampering
|
#### Tampering
|
||||||
|
|
||||||
|
|
@ -72,7 +77,7 @@ Not applicable - Cagebreak must allow system manipulation for user software.
|
||||||
Not applicable - There are no prohibited operations (See Tampering above.).
|
Not applicable - There are no prohibited operations (See Tampering above.).
|
||||||
While cagebreak does send events over documented channels there is no logging
|
While cagebreak does send events over documented channels there is no logging
|
||||||
activated by default, though, of course, this can be changed by the user
|
activated by default, though, of course, this can be changed by the user
|
||||||
by logging socket output for example.
|
by logging socket output (if enabled) for example.
|
||||||
|
|
||||||
#### Information Disclosure
|
#### Information Disclosure
|
||||||
|
|
||||||
|
|
@ -82,10 +87,25 @@ and any software run by the user may exfiltrate any data the user has access to.
|
||||||
#### Denial of Service
|
#### Denial of Service
|
||||||
|
|
||||||
Not applicable - Cagebreak offers functionality to terminate itself, which is
|
Not applicable - Cagebreak offers functionality to terminate itself, which is
|
||||||
available to all user software over the socket.
|
available to all user software over the socket if the socket is enabled.
|
||||||
|
|
||||||
#### Elevation of privilege
|
#### Elevation of Privilege
|
||||||
|
|
||||||
Software may gain arbitrary code execution rights if it has access to the
|
Software may gain arbitrary code execution rights if it has access to the
|
||||||
Cagebreak socket. Privilege escalation to root is unlikely since privileges
|
Cagebreak socket. Privilege escalation to root is unlikely since privileges
|
||||||
are dropped before any user input is accepted.
|
are dropped before any user input is accepted.
|
||||||
|
|
||||||
|
## GPG Keys of the Cagebreak Repository
|
||||||
|
|
||||||
|
All Cagebreak project keys are found under keys/ in the cagebreak
|
||||||
|
repository (the public keys anyway).
|
||||||
|
|
||||||
|
The most trusted keys of the Cagebreak project are its signing keys,
|
||||||
|
all signing keys are signed by at least one of its predecessors and at
|
||||||
|
least one non-expired signing key is used at the time of release to
|
||||||
|
sign the commit tag and the release code tarball.
|
||||||
|
|
||||||
|
Signing keys are also used to lend credence to other keys in the Cagebreak
|
||||||
|
project, such as the keys for email correspondence and the key used in the
|
||||||
|
cagebreak-pkgbuild repository.
|
||||||
|
|
||||||
|
|
|
||||||
332
cagebreak.c
332
cagebreak.c
|
|
@ -1,12 +1,8 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200812L
|
#define _POSIX_C_SOURCE 200812L
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
|
@ -16,8 +12,10 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/render/allocator.h>
|
#include <wlr/render/allocator.h>
|
||||||
|
|
@ -31,16 +29,19 @@
|
||||||
#include <wlr/types/wlr_idle.h>
|
#include <wlr/types/wlr_idle.h>
|
||||||
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
|
#include <wlr/types/wlr_presentation_time.h>
|
||||||
|
#include <wlr/types/wlr_primary_selection_v1.h>
|
||||||
|
#include <wlr/types/wlr_scene.h>
|
||||||
#include <wlr/types/wlr_screencopy_v1.h>
|
#include <wlr/types/wlr_screencopy_v1.h>
|
||||||
#include <wlr/types/wlr_server_decoration.h>
|
#include <wlr/types/wlr_server_decoration.h>
|
||||||
#if CG_HAS_XWAYLAND
|
#include <wlr/types/wlr_subcompositor.h>
|
||||||
#include <wlr/types/wlr_xcursor_manager.h>
|
#include <wlr/types/wlr_viewporter.h>
|
||||||
#endif
|
|
||||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||||
#include <wlr/types/wlr_xdg_output_v1.h>
|
#include <wlr/types/wlr_xdg_output_v1.h>
|
||||||
#include <wlr/types/wlr_xdg_shell.h>
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#if CG_HAS_XWAYLAND
|
#if CG_HAS_XWAYLAND
|
||||||
|
#include <wlr/types/wlr_xcursor_manager.h>
|
||||||
#include <wlr/xwayland.h>
|
#include <wlr/xwayland.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -53,6 +54,7 @@
|
||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
#include "seat.h"
|
#include "seat.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
#include "workspace.h"
|
||||||
#include "xdg_shell.h"
|
#include "xdg_shell.h"
|
||||||
#if CG_HAS_XWAYLAND
|
#if CG_HAS_XWAYLAND
|
||||||
#include "xwayland.h"
|
#include "xwayland.h"
|
||||||
|
|
@ -123,37 +125,21 @@ usage(FILE *file, const char *const cage) {
|
||||||
fprintf(file,
|
fprintf(file,
|
||||||
"Usage: %s [OPTIONS]\n"
|
"Usage: %s [OPTIONS]\n"
|
||||||
"\n"
|
"\n"
|
||||||
" -r\t Rotate the output 90 degrees clockwise, specify up to three "
|
" -c <path>\t Load configuration file from <path>\n"
|
||||||
"times\n"
|
" -e\t\t Enable socket\n"
|
||||||
#ifdef DEBUG
|
" -h\t\t Display this help message\n"
|
||||||
" -D\t Turn on damage tracking debugging\n"
|
" -s\t\t Show information about the current setup and exit\n"
|
||||||
#endif
|
" -v\t\t Show the version number and exit\n",
|
||||||
" -h\t Display this help message\n"
|
|
||||||
" -v\t Show the version number and exit\n"
|
|
||||||
" -s\t Show information about the current setup and exit\n",
|
|
||||||
cage);
|
cage);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_args(struct cg_server *server, int argc, char *argv[]) {
|
parse_args(struct cg_server *server, int argc, char *argv[],
|
||||||
|
char **config_path) {
|
||||||
int c;
|
int c;
|
||||||
#ifdef DEBUG
|
server->enable_socket = false;
|
||||||
while((c = getopt(argc, argv, "rDhvs")) != -1) {
|
while((c = getopt(argc, argv, "c:hvse")) != -1) {
|
||||||
#else
|
|
||||||
while((c = getopt(argc, argv, "rhvs")) != -1) {
|
|
||||||
#endif
|
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'r':
|
|
||||||
server->output_transform++;
|
|
||||||
if(server->output_transform > WL_OUTPUT_TRANSFORM_270) {
|
|
||||||
server->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#ifdef DEBUG
|
|
||||||
case 'D':
|
|
||||||
server->debug_damage_tracking = true;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case 'h':
|
case 'h':
|
||||||
usage(stdout, argv[0]);
|
usage(stdout, argv[0]);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -163,6 +149,15 @@ parse_args(struct cg_server *server, int argc, char *argv[]) {
|
||||||
case 's':
|
case 's':
|
||||||
show_info = true;
|
show_info = true;
|
||||||
break;
|
break;
|
||||||
|
case 'c':
|
||||||
|
if(optarg != NULL) {
|
||||||
|
*config_path = strdup(optarg);
|
||||||
|
optarg = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
server->enable_socket = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage(stderr, argv[0]);
|
usage(stderr, argv[0]);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -187,9 +182,28 @@ set_configuration(struct cg_server *server,
|
||||||
config_file_path);
|
config_file_path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
char line[MAX_LINE_SIZE * sizeof(char)];
|
uint32_t line_length = 64;
|
||||||
for(unsigned int line_num = 1;
|
char *line = calloc(line_length, sizeof(char));
|
||||||
fgets(line, MAX_LINE_SIZE, config_file) != NULL; ++line_num) {
|
for(unsigned int line_num = 1;; ++line_num) {
|
||||||
|
while(true) {
|
||||||
|
if(fgets(line + strlen(line), line_length - strlen(line),
|
||||||
|
config_file) == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(strcspn(line, "\n") != line_length - 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line_length *= 2;
|
||||||
|
line = reallocarray(line, line_length, sizeof(char));
|
||||||
|
if(line == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Could not allocate buffer for reading "
|
||||||
|
"configuration file.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(strlen(line) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
line[strcspn(line, "\n")] = '\0';
|
line[strcspn(line, "\n")] = '\0';
|
||||||
if(*line != '\0' && *line != '#') {
|
if(*line != '\0' && *line != '#') {
|
||||||
char *errstr;
|
char *errstr;
|
||||||
|
|
@ -200,16 +214,22 @@ set_configuration(struct cg_server *server,
|
||||||
if(errstr != NULL) {
|
if(errstr != NULL) {
|
||||||
free(errstr);
|
free(errstr);
|
||||||
}
|
}
|
||||||
|
free(line);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
memset(line, 0, line_length * sizeof(char));
|
||||||
}
|
}
|
||||||
|
free(line);
|
||||||
fclose(config_file);
|
fclose(config_file);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
get_config_file() {
|
get_config_file(char *udef_path) {
|
||||||
|
if(udef_path != NULL) {
|
||||||
|
return strdup(udef_path);
|
||||||
|
}
|
||||||
const char *config_home_path = getenv("XDG_CONFIG_HOME");
|
const char *config_home_path = getenv("XDG_CONFIG_HOME");
|
||||||
char *addition = "/cagebreak/config";
|
char *addition = "/cagebreak/config";
|
||||||
if(config_home_path == NULL || config_home_path[0] == '\0') {
|
if(config_home_path == NULL || config_home_path[0] == '\0') {
|
||||||
|
|
@ -236,25 +256,30 @@ main(int argc, char *argv[]) {
|
||||||
struct wl_event_source *sigint_source = NULL;
|
struct wl_event_source *sigint_source = NULL;
|
||||||
struct wl_event_source *sigterm_source = NULL;
|
struct wl_event_source *sigterm_source = NULL;
|
||||||
struct wl_event_source *sigalrm_source = NULL;
|
struct wl_event_source *sigalrm_source = NULL;
|
||||||
|
struct wl_event_source *sigpipe_source = NULL;
|
||||||
struct wlr_backend *backend = NULL;
|
struct wlr_backend *backend = NULL;
|
||||||
struct wlr_compositor *compositor = NULL;
|
struct wlr_compositor *compositor = NULL;
|
||||||
|
struct wlr_subcompositor *subcompositor = NULL;
|
||||||
struct wlr_data_device_manager *data_device_manager = NULL;
|
struct wlr_data_device_manager *data_device_manager = NULL;
|
||||||
struct wlr_server_decoration_manager *server_decoration_manager = NULL;
|
struct wlr_server_decoration_manager *server_decoration_manager = NULL;
|
||||||
struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager = NULL;
|
struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager = NULL;
|
||||||
struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager = NULL;
|
struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager = NULL;
|
||||||
struct wlr_screencopy_manager_v1 *screencopy_manager = NULL;
|
struct wlr_screencopy_manager_v1 *screencopy_manager = NULL;
|
||||||
struct wlr_data_control_manager_v1 *data_control_manager = NULL;
|
struct wlr_data_control_manager_v1 *data_control_manager = NULL;
|
||||||
|
struct wlr_viewporter *viewporter = NULL;
|
||||||
|
struct wlr_presentation *presentation = NULL;
|
||||||
struct wlr_xdg_output_manager_v1 *output_manager = NULL;
|
struct wlr_xdg_output_manager_v1 *output_manager = NULL;
|
||||||
struct wlr_gamma_control_manager_v1 *gamma_control_manager = NULL;
|
struct wlr_gamma_control_manager_v1 *gamma_control_manager = NULL;
|
||||||
struct wlr_xdg_shell *xdg_shell = NULL;
|
struct wlr_xdg_shell *xdg_shell = NULL;
|
||||||
#if CG_HAS_XWAYLAND
|
wl_list_init(&server.input_config);
|
||||||
struct wlr_xwayland *xwayland = NULL;
|
wl_list_init(&server.output_config);
|
||||||
struct wlr_xcursor_manager *xcursor_manager = NULL;
|
wl_list_init(&server.output_priorities);
|
||||||
#endif
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if(!parse_args(&server, argc, argv)) {
|
char *config_path = NULL;
|
||||||
return 1;
|
if(!parse_args(&server, argc, argv, &config_path)) {
|
||||||
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
@ -263,25 +288,34 @@ main(int argc, char *argv[]) {
|
||||||
wlr_log_init(WLR_ERROR, NULL);
|
wlr_log_init(WLR_ERROR, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
wl_list_init(&server.input_config);
|
|
||||||
wl_list_init(&server.output_config);
|
|
||||||
|
|
||||||
server.modes = malloc(4 * sizeof(char *));
|
server.modes = malloc(4 * sizeof(char *));
|
||||||
if(!server.modes) {
|
if(!server.modes) {
|
||||||
wlr_log(WLR_ERROR, "Error allocating mode array");
|
wlr_log(WLR_ERROR, "Error allocating mode array");
|
||||||
return -1;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wayland requires XDG_RUNTIME_DIR to be set. */
|
/* Wayland requires XDG_RUNTIME_DIR to be set. */
|
||||||
if(!getenv("XDG_RUNTIME_DIR")) {
|
if(!getenv("XDG_RUNTIME_DIR")) {
|
||||||
wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is not set in the environment");
|
wlr_log(WLR_INFO, "XDG_RUNTIME_DIR is not set in the environment");
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server.wl_display = wl_display_create();
|
server.wl_display = wl_display_create();
|
||||||
if(!server.wl_display) {
|
if(!server.wl_display) {
|
||||||
wlr_log(WLR_ERROR, "Cannot allocate a Wayland display");
|
wlr_log(WLR_ERROR, "Cannot allocate a Wayland display");
|
||||||
return 1;
|
free(server.modes);
|
||||||
|
server.modes = NULL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.xcursor_size = XCURSOR_SIZE;
|
||||||
|
const char *env_cursor_size = getenv("XCURSOR_SIZE");
|
||||||
|
if(env_cursor_size && strlen(env_cursor_size) > 0) {
|
||||||
|
errno = 0;
|
||||||
|
char *end;
|
||||||
|
unsigned size = strtoul(env_cursor_size, &end, 10);
|
||||||
|
if(!*end && errno == 0) {
|
||||||
|
server.xcursor_size = size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server.running = true;
|
server.running = true;
|
||||||
|
|
@ -293,10 +327,12 @@ main(int argc, char *argv[]) {
|
||||||
if(server.modes[0] == NULL || server.modes[1] == NULL ||
|
if(server.modes[0] == NULL || server.modes[1] == NULL ||
|
||||||
server.modes[2] == NULL) {
|
server.modes[2] == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Error allocating default modes");
|
wlr_log(WLR_ERROR, "Error allocating default modes");
|
||||||
return 1;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.nws = 1;
|
server.nws = 1;
|
||||||
|
server.views_curr_id = 1;
|
||||||
|
server.tiles_curr_id = 1;
|
||||||
server.message_config.fg_color[0] = 0.0;
|
server.message_config.fg_color[0] = 0.0;
|
||||||
server.message_config.fg_color[1] = 0.0;
|
server.message_config.fg_color[1] = 0.0;
|
||||||
server.message_config.fg_color[2] = 0.0;
|
server.message_config.fg_color[2] = 0.0;
|
||||||
|
|
@ -317,6 +353,8 @@ main(int argc, char *argv[]) {
|
||||||
wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server);
|
wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server);
|
||||||
sigalrm_source =
|
sigalrm_source =
|
||||||
wl_event_loop_add_signal(event_loop, SIGALRM, handle_signal, &server);
|
wl_event_loop_add_signal(event_loop, SIGALRM, handle_signal, &server);
|
||||||
|
sigpipe_source =
|
||||||
|
wl_event_loop_add_signal(event_loop, SIGPIPE, handle_signal, &server);
|
||||||
server.event_loop = event_loop;
|
server.event_loop = event_loop;
|
||||||
|
|
||||||
backend = wlr_backend_autocreate(server.wl_display);
|
backend = wlr_backend_autocreate(server.wl_display);
|
||||||
|
|
@ -345,6 +383,7 @@ main(int argc, char *argv[]) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.allocator =
|
server.allocator =
|
||||||
wlr_allocator_autocreate(server.backend, server.renderer);
|
wlr_allocator_autocreate(server.backend, server.renderer);
|
||||||
if(!server.allocator) {
|
if(!server.allocator) {
|
||||||
|
|
@ -352,6 +391,7 @@ main(int argc, char *argv[]) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
|
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
|
||||||
|
|
||||||
server.bg_color = (float[4]){0, 0, 0, 1};
|
server.bg_color = (float[4]){0, 0, 0, 1};
|
||||||
|
|
@ -365,6 +405,20 @@ main(int argc, char *argv[]) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ipc_init(&server) != 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to initialize IPC");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.scene = wlr_scene_create();
|
||||||
|
if(!server.scene) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to create scene");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
wlr_scene_attach_output_layout(server.scene, server.output_layout);
|
||||||
|
|
||||||
compositor = wlr_compositor_create(server.wl_display, server.renderer);
|
compositor = wlr_compositor_create(server.wl_display, server.renderer);
|
||||||
if(!compositor) {
|
if(!compositor) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the wlroots compositor");
|
wlr_log(WLR_ERROR, "Unable to create the wlroots compositor");
|
||||||
|
|
@ -372,6 +426,13 @@ main(int argc, char *argv[]) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subcompositor = wlr_subcompositor_create(server.wl_display);
|
||||||
|
if(!subcompositor) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to create the wlroots subcompositor");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
data_device_manager = wlr_data_device_manager_create(server.wl_display);
|
data_device_manager = wlr_data_device_manager_create(server.wl_display);
|
||||||
if(!data_device_manager) {
|
if(!data_device_manager) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the data device manager");
|
wlr_log(WLR_ERROR, "Unable to create the data device manager");
|
||||||
|
|
@ -420,7 +481,7 @@ main(int argc, char *argv[]) {
|
||||||
&server.new_idle_inhibitor_v1);
|
&server.new_idle_inhibitor_v1);
|
||||||
wl_list_init(&server.inhibitors);
|
wl_list_init(&server.inhibitors);
|
||||||
|
|
||||||
xdg_shell = wlr_xdg_shell_create(server.wl_display);
|
xdg_shell = wlr_xdg_shell_create(server.wl_display, 3);
|
||||||
if(!xdg_shell) {
|
if(!xdg_shell) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the XDG shell interface");
|
wlr_log(WLR_ERROR, "Unable to create the XDG shell interface");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
@ -451,6 +512,21 @@ main(int argc, char *argv[]) {
|
||||||
wlr_server_decoration_manager_set_default_mode(
|
wlr_server_decoration_manager_set_default_mode(
|
||||||
server_decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
|
server_decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
|
||||||
|
|
||||||
|
viewporter = wlr_viewporter_create(server.wl_display);
|
||||||
|
if(!viewporter) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to create the viewporter interface");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
presentation = wlr_presentation_create(server.wl_display, server.backend);
|
||||||
|
if(!presentation) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to create the presentation interface");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
wlr_scene_set_presentation(server.scene, presentation);
|
||||||
|
|
||||||
export_dmabuf_manager =
|
export_dmabuf_manager =
|
||||||
wlr_export_dmabuf_manager_v1_create(server.wl_display);
|
wlr_export_dmabuf_manager_v1_create(server.wl_display);
|
||||||
if(!export_dmabuf_manager) {
|
if(!export_dmabuf_manager) {
|
||||||
|
|
@ -474,6 +550,13 @@ main(int argc, char *argv[]) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!wlr_primary_selection_v1_device_manager_create(server.wl_display)) {
|
||||||
|
wlr_log(WLR_ERROR,
|
||||||
|
"Unable to create the primary selection device manager");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
gamma_control_manager =
|
gamma_control_manager =
|
||||||
wlr_gamma_control_manager_v1_create(server.wl_display);
|
wlr_gamma_control_manager_v1_create(server.wl_display);
|
||||||
if(!gamma_control_manager) {
|
if(!gamma_control_manager) {
|
||||||
|
|
@ -483,40 +566,32 @@ main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CG_HAS_XWAYLAND
|
#if CG_HAS_XWAYLAND
|
||||||
xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
|
server.xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
|
||||||
if(!xwayland) {
|
if(!server.xwayland) {
|
||||||
wlr_log(WLR_ERROR, "Cannot create XWayland server");
|
wlr_log(WLR_ERROR, "Cannot create XWayland server");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
server.new_xwayland_surface.notify = handle_xwayland_surface_new;
|
server.new_xwayland_surface.notify = handle_xwayland_surface_new;
|
||||||
wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface);
|
wl_signal_add(&server.xwayland->events.new_surface,
|
||||||
|
&server.new_xwayland_surface);
|
||||||
|
|
||||||
xcursor_manager = wlr_xcursor_manager_create(DEFAULT_XCURSOR, XCURSOR_SIZE);
|
if(setenv("DISPLAY", server.xwayland->display_name, true) < 0) {
|
||||||
if(!xcursor_manager) {
|
|
||||||
wlr_log(WLR_ERROR, "Cannot create XWayland XCursor manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(setenv("DISPLAY", xwayland->display_name, true) < 0) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to set DISPLAY for XWayland.",
|
wlr_log_errno(WLR_ERROR, "Unable to set DISPLAY for XWayland.",
|
||||||
"Clients may not be able to connect");
|
"Clients may not be able to connect");
|
||||||
} else {
|
} else {
|
||||||
wlr_log(WLR_DEBUG, "XWayland is running on display %s",
|
wlr_log(WLR_DEBUG, "XWayland is running on display %s",
|
||||||
xwayland->display_name);
|
server.xwayland->display_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!wlr_xcursor_manager_load(xcursor_manager, 1)) {
|
struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
|
||||||
wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme");
|
server.seat->xcursor_manager, DEFAULT_XCURSOR, 1);
|
||||||
}
|
|
||||||
struct wlr_xcursor *xcursor =
|
|
||||||
wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1);
|
|
||||||
if(xcursor) {
|
if(xcursor) {
|
||||||
struct wlr_xcursor_image *image = xcursor->images[0];
|
struct wlr_xcursor_image *image = xcursor->images[0];
|
||||||
wlr_xwayland_set_cursor(xwayland, image->buffer, image->width * 4,
|
wlr_xwayland_set_cursor(server.xwayland, image->buffer,
|
||||||
image->width, image->height, image->hotspot_x,
|
image->width * 4, image->width, image->height,
|
||||||
image->hotspot_y);
|
image->hotspot_x, image->hotspot_y);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -537,13 +612,13 @@ main(int argc, char *argv[]) {
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.",
|
wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.",
|
||||||
"Clients may not be able to connect");
|
"Clients may not be able to connect");
|
||||||
} else {
|
} else {
|
||||||
wlr_log(WLR_DEBUG,
|
fprintf(stderr,
|
||||||
"Cagebreak " CG_VERSION " is running on Wayland display %s",
|
"Cagebreak " CG_VERSION " is running on Wayland display %s\n",
|
||||||
socket);
|
socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CG_HAS_XWAYLAND
|
#if CG_HAS_XWAYLAND
|
||||||
wlr_xwayland_set_seat(xwayland, server.seat->seat);
|
wlr_xwayland_set_seat(server.xwayland, server.seat->seat);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(show_info) {
|
if(show_info) {
|
||||||
|
|
@ -557,14 +632,8 @@ main(int argc, char *argv[]) {
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ipc_init(&server) != 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to initialize IPC");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // config_file should only be visible as long as it is valid
|
{ // config_file should only be visible as long as it is valid
|
||||||
char *config_file = get_config_file();
|
char *config_file = get_config_file(config_path);
|
||||||
if(config_file == NULL) {
|
if(config_file == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Unable to get path to config file");
|
wlr_log(WLR_ERROR, "Unable to get path to config file");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
@ -573,27 +642,35 @@ main(int argc, char *argv[]) {
|
||||||
int conf_ret = set_configuration(&server, config_file);
|
int conf_ret = set_configuration(&server, config_file);
|
||||||
|
|
||||||
// Configurtion file not found
|
// Configurtion file not found
|
||||||
if(conf_ret == 1) {
|
if(conf_ret != 0) {
|
||||||
char *default_conf = "/etc/xdg/cagebreak/config";
|
if(config_file == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Loading default configuration file: \"%s\"",
|
char *default_conf = "/etc/xdg/cagebreak/config";
|
||||||
default_conf);
|
wlr_log(WLR_INFO, "Loading default configuration file: \"%s\"",
|
||||||
conf_ret = set_configuration(&server, default_conf);
|
default_conf);
|
||||||
|
conf_ret = set_configuration(&server, default_conf);
|
||||||
|
} else {
|
||||||
|
conf_ret = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(conf_ret != 0) {
|
free(config_file);
|
||||||
free(config_file);
|
if(conf_ret != 0 || !server.running) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
free(config_file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
struct wl_list tmp_list;
|
||||||
|
wl_list_init(&tmp_list);
|
||||||
|
wl_list_insert_list(&tmp_list, &server.outputs);
|
||||||
|
wl_list_init(&server.outputs);
|
||||||
struct cg_output *output, *output_tmp;
|
struct cg_output *output, *output_tmp;
|
||||||
wl_list_for_each_safe(output, output_tmp, &server.outputs, link) {
|
wl_list_for_each_safe(output, output_tmp, &tmp_list, link) {
|
||||||
output_configure(&server, output);
|
output_configure(&server, output);
|
||||||
}
|
}
|
||||||
|
server.curr_output =
|
||||||
|
wl_container_of(server.outputs.next, server.curr_output, link);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Place the cursor to the top left of the output layout. */
|
/* Place the cursor to the top left of the output layout. */
|
||||||
|
|
@ -601,21 +678,23 @@ main(int argc, char *argv[]) {
|
||||||
|
|
||||||
wl_display_run(server.wl_display);
|
wl_display_run(server.wl_display);
|
||||||
|
|
||||||
#if CG_HAS_XWAYLAND
|
|
||||||
wlr_xwayland_destroy(xwayland);
|
|
||||||
wlr_xcursor_manager_destroy(xcursor_manager);
|
|
||||||
#endif
|
|
||||||
wl_display_destroy_clients(server.wl_display);
|
wl_display_destroy_clients(server.wl_display);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
|
if(server.modes != NULL) {
|
||||||
#if CG_HAS_FANALYZE
|
#if CG_HAS_FANALYZE
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wanalyzer-double-free"
|
#pragma GCC diagnostic ignored "-Wanalyzer-double-free"
|
||||||
#endif
|
#endif
|
||||||
for(unsigned int i = 0; server.modes[i] != NULL; ++i) {
|
for(unsigned int i = 0; server.modes[i] != NULL; ++i) {
|
||||||
free(server.modes[i]);
|
free(server.modes[i]);
|
||||||
|
}
|
||||||
|
free(server.modes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config_path != NULL) {
|
||||||
|
free(config_path);
|
||||||
}
|
}
|
||||||
free(server.modes);
|
|
||||||
|
|
||||||
struct cg_output_config *output_config, *output_config_tmp;
|
struct cg_output_config *output_config, *output_config_tmp;
|
||||||
wl_list_for_each_safe(output_config, output_config_tmp,
|
wl_list_for_each_safe(output_config, output_config_tmp,
|
||||||
|
|
@ -625,19 +704,52 @@ end:
|
||||||
free(output_config);
|
free(output_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
keybinding_list_free(server.keybindings);
|
struct cg_input_config *input_config, *input_config_tmp;
|
||||||
wl_event_source_remove(sigint_source);
|
wl_list_for_each_safe(input_config, input_config_tmp, &server.input_config,
|
||||||
wl_event_source_remove(sigterm_source);
|
link) {
|
||||||
wl_event_source_remove(sigalrm_source);
|
wl_list_remove(&input_config->link);
|
||||||
|
if(input_config->identifier != NULL) {
|
||||||
|
free(input_config->identifier);
|
||||||
|
}
|
||||||
|
free(input_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(server.keybindings != NULL) {
|
||||||
|
keybinding_list_free(server.keybindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(server.message_config.font != NULL) {
|
||||||
|
free(server.message_config.font);
|
||||||
|
}
|
||||||
|
server.running = false;
|
||||||
|
if(server.seat != NULL) {
|
||||||
|
seat_destroy(server.seat);
|
||||||
|
}
|
||||||
|
#if CG_HAS_XWAYLAND
|
||||||
|
if(server.xwayland != NULL) {
|
||||||
|
wlr_xwayland_destroy(server.xwayland);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(sigint_source != NULL) {
|
||||||
|
wl_event_source_remove(sigint_source);
|
||||||
|
wl_event_source_remove(sigterm_source);
|
||||||
|
wl_event_source_remove(sigalrm_source);
|
||||||
|
wl_event_source_remove(sigpipe_source);
|
||||||
|
}
|
||||||
|
|
||||||
seat_destroy(server.seat);
|
|
||||||
/* This function is not null-safe, but we only ever get here
|
/* This function is not null-safe, but we only ever get here
|
||||||
with a proper wl_display. */
|
with a proper wl_display. */
|
||||||
wl_display_destroy(server.wl_display);
|
if(server.wl_display != NULL) {
|
||||||
wlr_output_layout_destroy(server.output_layout);
|
wl_display_destroy(server.wl_display);
|
||||||
|
}
|
||||||
|
if(server.output_layout != NULL) {
|
||||||
|
wlr_output_layout_destroy(server.output_layout);
|
||||||
|
}
|
||||||
|
|
||||||
free(server.input);
|
if(server.input != NULL) {
|
||||||
free(server.message_config.font);
|
free(server.input);
|
||||||
|
}
|
||||||
pango_cairo_font_map_set_default(NULL);
|
pango_cairo_font_map_set_default(NULL);
|
||||||
cairo_debug_reset_static_data();
|
cairo_debug_reset_static_data();
|
||||||
FcFini();
|
FcFini();
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef CG_CONFIG_H
|
#ifndef CG_CONFIG_H
|
||||||
#define CG_CONFIG_H
|
#define CG_CONFIG_H
|
||||||
|
|
||||||
|
|
@ -6,4 +9,6 @@
|
||||||
|
|
||||||
#mesondefine CG_VERSION
|
#mesondefine CG_VERSION
|
||||||
|
|
||||||
|
#define MAX_NESTING_LEVEL 50
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
18
example_scripts/cb_script_header.sh
Normal file
18
example_scripts/cb_script_header.sh
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Copyright 2023, project-repo and the cagebreak contributors
|
||||||
|
# SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
|
named_pipe_send="$(mktemp -u)"
|
||||||
|
named_pipe_recv="$(mktemp -u)"
|
||||||
|
mkfifo "${named_pipe_send}"
|
||||||
|
mkfifo "${named_pipe_recv}"
|
||||||
|
nc -U "${CAGEBREAK_SOCKET}" < "${named_pipe_send}" > "${named_pipe_recv}"&
|
||||||
|
# The file descriptor 3 is set up to send commands to cagebreak and file
|
||||||
|
# descriptor 4 can be used to read events. Notice that events will pile up in
|
||||||
|
# file descriptor 4, so it is a good idea to continuously read from it or to
|
||||||
|
# clear it before starting a new transaction.
|
||||||
|
exec 3>"${named_pipe_send}"
|
||||||
|
exec 4<"${named_pipe_recv}"
|
||||||
|
# When the script exits, the os will clean up the pipe
|
||||||
|
rm "${named_pipe_recv}"
|
||||||
|
rm "${named_pipe_send}"
|
||||||
23
example_scripts/focus_follows_cursor.sh
Normal file
23
example_scripts/focus_follows_cursor.sh
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Copyright 2023, project-repo and the cagebreak contributors
|
||||||
|
# SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
|
# This script uses the cagebreak socket to modify the compositor's behaviour in
|
||||||
|
# such a way that the currently focussed tile follows the cursor, i.e. that the
|
||||||
|
# currently focussed tile is always the one within which the cursor is located.
|
||||||
|
|
||||||
|
# Start up the ipc link
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/cb_script_header.sh"
|
||||||
|
|
||||||
|
while IFS= read -r -d $'\0' event
|
||||||
|
do
|
||||||
|
# Remove the cg-ipc header
|
||||||
|
event="${event:6}"
|
||||||
|
if [[ "$(echo "${event}" | jq ".event_name")" = "\"cursor_switch_tile\"" ]]
|
||||||
|
then
|
||||||
|
new_tile="$(echo "${event}"|jq -r ".new_tile")"
|
||||||
|
new_output_id="$(echo "${event}"|jq -r ".new_output_id")"
|
||||||
|
# Send the new tile to focus to cagebreak
|
||||||
|
echo -e "screen ${new_output_id}\nfocus_tile ${new_tile}" >&3
|
||||||
|
fi
|
||||||
|
done <&4
|
||||||
25
example_scripts/show_workspace_views.sh
Normal file
25
example_scripts/show_workspace_views.sh
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
# SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
|
# This script displays the process names of the views on the current workspace.
|
||||||
|
|
||||||
|
# Start up the ipc link
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/cb_script_header.sh"
|
||||||
|
|
||||||
|
echo "dump" >&3
|
||||||
|
|
||||||
|
while IFS= read -r -d $'\0' event
|
||||||
|
do
|
||||||
|
# Remove the cg-ipc header
|
||||||
|
event="${event:6}"
|
||||||
|
if [[ "$(echo "${event}" | jq ".event_name")" = "\"dump\"" ]]
|
||||||
|
then
|
||||||
|
curr_output="$(echo "${event}"|jq ".curr_output")"
|
||||||
|
curr_workspace="$(echo "${event}"|jq -r ".outputs.${curr_output}.curr_workspace")"
|
||||||
|
# Print the process names of the view on the current workspace. jq retrieves
|
||||||
|
# their PID and ps is then used to retrieve the process names.
|
||||||
|
(echo -n "message ";ps -o comm=Command -p $(echo "${event}"|jq -r ".outputs.${curr_output}.workspaces[$((curr_workspace-1))].views[].pid")|tail +2|sed ':a; N; $!ba; s/\n/||/g') >&3
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done <&4
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
# Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
# SPDX -License-Identifier: MIT
|
||||||
######################
|
######################
|
||||||
# General settings an key bindings
|
# General settings an key bindings
|
||||||
######################
|
######################
|
||||||
|
|
@ -41,6 +43,9 @@ definekey resize j resizedown
|
||||||
definekey resize k resizeup
|
definekey resize k resizeup
|
||||||
definekey resize Escape setmode top
|
definekey resize Escape setmode top
|
||||||
|
|
||||||
|
#unhide cursor (default)
|
||||||
|
cursor enable
|
||||||
|
|
||||||
######################
|
######################
|
||||||
#Workspaces
|
#Workspaces
|
||||||
######################
|
######################
|
||||||
|
|
@ -95,6 +100,7 @@ definekey top XF86MonBrightnessUp exec xbacklight -inc 1
|
||||||
#output eDP-1 disable
|
#output eDP-1 disable
|
||||||
#output eDP-1 enable
|
#output eDP-1 enable
|
||||||
#output eDP-1 prio 1
|
#output eDP-1 prio 1
|
||||||
|
#output eDP-2 pos 0 0 res 1366x768 rate 60 scale 2.0
|
||||||
|
|
||||||
#####################
|
#####################
|
||||||
#Input configuration
|
#Input configuration
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
/* This file is used by the fuzzer in order to prevent executing shell commands.
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
*/
|
// SPDX -License-Identifier: MIT
|
||||||
|
// This file is used by the fuzzer in order to prevent executing shell commands.
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include "../output.h"
|
#include "../output.h"
|
||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
|
|
|
||||||
278
fuzz/fuzz-lib.c
278
fuzz/fuzz-lib.c
|
|
@ -1,10 +1,5 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200812L
|
#define _POSIX_C_SOURCE 200812L
|
||||||
|
|
||||||
|
|
@ -35,8 +30,12 @@
|
||||||
#include <wlr/types/wlr_keyboard_group.h>
|
#include <wlr/types/wlr_keyboard_group.h>
|
||||||
#include <wlr/types/wlr_output_damage.h>
|
#include <wlr/types/wlr_output_damage.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
|
#include <wlr/types/wlr_presentation_time.h>
|
||||||
|
#include <wlr/types/wlr_primary_selection_v1.h>
|
||||||
|
#include <wlr/types/wlr_scene.h>
|
||||||
#include <wlr/types/wlr_screencopy_v1.h>
|
#include <wlr/types/wlr_screencopy_v1.h>
|
||||||
#include <wlr/types/wlr_server_decoration.h>
|
#include <wlr/types/wlr_server_decoration.h>
|
||||||
|
#include <wlr/types/wlr_viewporter.h>
|
||||||
#if CG_HAS_XWAYLAND
|
#if CG_HAS_XWAYLAND
|
||||||
#include <wlr/types/wlr_xcursor_manager.h>
|
#include <wlr/types/wlr_xcursor_manager.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -88,15 +87,6 @@ drop_permissions(void) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
parse_args(struct cg_server *server, int argc, char *argv[]) {
|
|
||||||
server->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
|
||||||
#ifdef DEBUG
|
|
||||||
server->debug_damage_tracking = false;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
cleanup() {
|
cleanup() {
|
||||||
server.running = false;
|
server.running = false;
|
||||||
|
|
@ -120,13 +110,11 @@ cleanup() {
|
||||||
seat_destroy(server.seat);
|
seat_destroy(server.seat);
|
||||||
/* This function is not null-safe, but we only ever get here
|
/* This function is not null-safe, but we only ever get here
|
||||||
with a proper wl_display. */
|
with a proper wl_display. */
|
||||||
wl_display_destroy(server.wl_display);
|
|
||||||
wlr_output_layout_destroy(server.output_layout);
|
wlr_output_layout_destroy(server.output_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
LLVMFuzzerInitialize(int *argc, char ***argv) {
|
LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
struct wl_event_loop *event_loop = NULL;
|
|
||||||
struct wlr_backend *backend = NULL;
|
struct wlr_backend *backend = NULL;
|
||||||
struct wlr_compositor *compositor = NULL;
|
struct wlr_compositor *compositor = NULL;
|
||||||
struct wlr_data_device_manager *data_device_manager = NULL;
|
struct wlr_data_device_manager *data_device_manager = NULL;
|
||||||
|
|
@ -135,13 +123,21 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager = NULL;
|
struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager = NULL;
|
||||||
struct wlr_screencopy_manager_v1 *screencopy_manager = NULL;
|
struct wlr_screencopy_manager_v1 *screencopy_manager = NULL;
|
||||||
struct wlr_data_control_manager_v1 *data_control_manager = NULL;
|
struct wlr_data_control_manager_v1 *data_control_manager = NULL;
|
||||||
|
struct wlr_viewporter *viewporter = NULL;
|
||||||
|
struct wlr_presentation *presentation = NULL;
|
||||||
struct wlr_xdg_output_manager_v1 *output_manager = NULL;
|
struct wlr_xdg_output_manager_v1 *output_manager = NULL;
|
||||||
struct wlr_gamma_control_manager_v1 *gamma_control_manager = NULL;
|
struct wlr_gamma_control_manager_v1 *gamma_control_manager = NULL;
|
||||||
int ret = 0;
|
struct wlr_xdg_shell *xdg_shell = NULL;
|
||||||
|
#if CG_HAS_XWAYLAND
|
||||||
|
struct wlr_xwayland *xwayland = NULL;
|
||||||
|
#endif
|
||||||
|
wl_list_init(&server.input_config);
|
||||||
|
wl_list_init(&server.output_config);
|
||||||
|
wl_list_init(&server.output_priorities);
|
||||||
|
wl_list_init(&server.outputs);
|
||||||
|
wl_list_init(&server.disabled_outputs);
|
||||||
|
|
||||||
if(!parse_args(&server, *argc, *argv)) {
|
int ret = 0;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
wlr_log_init(WLR_DEBUG, NULL);
|
wlr_log_init(WLR_DEBUG, NULL);
|
||||||
|
|
@ -149,33 +145,63 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
wlr_log_init(WLR_ERROR, NULL);
|
wlr_log_init(WLR_ERROR, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
wl_list_init(&server.input_config);
|
server.modes = malloc(4 * sizeof(char *));
|
||||||
|
if(!server.modes) {
|
||||||
|
wlr_log(WLR_ERROR, "Error allocating mode array");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
/* Wayland requires XDG_RUNTIME_DIR to be set. */
|
/* Wayland requires XDG_RUNTIME_DIR to be set. */
|
||||||
if(!getenv("XDG_RUNTIME_DIR")) {
|
if(!getenv("XDG_RUNTIME_DIR")) {
|
||||||
wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is not set in the environment");
|
wlr_log(WLR_INFO, "XDG_RUNTIME_DIR is not set in the environment");
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server.wl_display = wl_display_create();
|
server.wl_display = wl_display_create();
|
||||||
if(!server.wl_display) {
|
if(!server.wl_display) {
|
||||||
wlr_log(WLR_ERROR, "Cannot allocate a Wayland display");
|
wlr_log(WLR_ERROR, "Cannot allocate a Wayland display");
|
||||||
return 1;
|
free(server.modes);
|
||||||
|
server.modes = NULL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.xcursor_size = XCURSOR_SIZE;
|
||||||
|
const char *env_cursor_size = getenv("XCURSOR_SIZE");
|
||||||
|
if(env_cursor_size && strlen(env_cursor_size) > 0) {
|
||||||
|
errno = 0;
|
||||||
|
char *end;
|
||||||
|
unsigned size = strtoul(env_cursor_size, &end, 10);
|
||||||
|
if(!*end && errno == 0) {
|
||||||
|
server.xcursor_size = size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server.running = true;
|
server.running = true;
|
||||||
|
|
||||||
server.modes = malloc(4 * sizeof(char *));
|
|
||||||
server.modes[0] = strdup("top");
|
server.modes[0] = strdup("top");
|
||||||
server.modes[1] = strdup("root");
|
server.modes[1] = strdup("root");
|
||||||
server.modes[2] = strdup("resize");
|
server.modes[2] = strdup("resize");
|
||||||
server.modes[3] = NULL;
|
server.modes[3] = NULL;
|
||||||
|
if(server.modes[0] == NULL || server.modes[1] == NULL ||
|
||||||
|
server.modes[2] == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Error allocating default modes");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
server.nws = 1;
|
server.nws = 1;
|
||||||
server.message_timeout = 2;
|
server.views_curr_id = 1;
|
||||||
|
server.tiles_curr_id = 1;
|
||||||
|
server.message_config.fg_color[0] = 0.0;
|
||||||
|
server.message_config.fg_color[1] = 0.0;
|
||||||
|
server.message_config.fg_color[2] = 0.0;
|
||||||
|
server.message_config.fg_color[3] = 1.0;
|
||||||
|
|
||||||
event_loop = wl_display_get_event_loop(server.wl_display);
|
server.message_config.bg_color[0] = 0.9;
|
||||||
server.event_loop = event_loop;
|
server.message_config.bg_color[1] = 0.85;
|
||||||
|
server.message_config.bg_color[2] = 0.85;
|
||||||
|
server.message_config.bg_color[3] = 1.0;
|
||||||
|
|
||||||
|
server.message_config.display_time = 2;
|
||||||
|
server.message_config.font = strdup("pango:Monospace 10");
|
||||||
|
|
||||||
backend = wlr_multi_backend_create(server.wl_display);
|
backend = wlr_multi_backend_create(server.wl_display);
|
||||||
if(!backend) {
|
if(!backend) {
|
||||||
|
|
@ -201,11 +227,6 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
goto end;
|
goto end;
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!drop_permissions()) {
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
server.keybindings = keybinding_list_init();
|
server.keybindings = keybinding_list_init();
|
||||||
if(server.keybindings == NULL || server.keybindings->keybindings == NULL) {
|
if(server.keybindings == NULL || server.keybindings->keybindings == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Unable to allocate keybindings");
|
wlr_log(WLR_ERROR, "Unable to allocate keybindings");
|
||||||
|
|
@ -213,14 +234,13 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_list_init(&server.output_config);
|
|
||||||
|
|
||||||
server.renderer = wlr_renderer_autocreate(backend);
|
server.renderer = wlr_renderer_autocreate(backend);
|
||||||
if(!server.renderer) {
|
if(!server.renderer) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the wlroots renderer");
|
wlr_log(WLR_ERROR, "Unable to create the wlroots renderer");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.allocator =
|
server.allocator =
|
||||||
wlr_allocator_autocreate(server.backend, server.renderer);
|
wlr_allocator_autocreate(server.backend, server.renderer);
|
||||||
if(!server.allocator) {
|
if(!server.allocator) {
|
||||||
|
|
@ -228,16 +248,10 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
|
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
|
||||||
|
|
||||||
server.bg_color = malloc(4 * sizeof(float));
|
server.bg_color = (float[4]){0, 0, 0, 1};
|
||||||
server.bg_color[0] = 0;
|
|
||||||
server.bg_color[1] = 0;
|
|
||||||
server.bg_color[2] = 0;
|
|
||||||
server.bg_color[3] = 1;
|
|
||||||
wl_list_init(&server.outputs);
|
|
||||||
wl_list_init(&server.disabled_outputs);
|
|
||||||
|
|
||||||
server.output_layout = wlr_output_layout_create();
|
server.output_layout = wlr_output_layout_create();
|
||||||
if(!server.output_layout) {
|
if(!server.output_layout) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create output layout");
|
wlr_log(WLR_ERROR, "Unable to create output layout");
|
||||||
|
|
@ -245,6 +259,27 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ipc_init(&server) != 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to initialize IPC");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.scene = wlr_scene_create();
|
||||||
|
if(!server.scene) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to create scene");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
wlr_scene_attach_output_layout(server.scene, server.output_layout);
|
||||||
|
|
||||||
|
compositor = wlr_compositor_create(server.wl_display, server.renderer);
|
||||||
|
if(!compositor) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to create the wlroots compositor");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
data_device_manager = wlr_data_device_manager_create(server.wl_display);
|
data_device_manager = wlr_data_device_manager_create(server.wl_display);
|
||||||
if(!data_device_manager) {
|
if(!data_device_manager) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the data device manager");
|
wlr_log(WLR_ERROR, "Unable to create the data device manager");
|
||||||
|
|
@ -293,7 +328,7 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
&server.new_idle_inhibitor_v1);
|
&server.new_idle_inhibitor_v1);
|
||||||
wl_list_init(&server.inhibitors);
|
wl_list_init(&server.inhibitors);
|
||||||
|
|
||||||
xdg_shell = wlr_xdg_shell_create(server.wl_display);
|
xdg_shell = wlr_xdg_shell_create(server.wl_display, 3);
|
||||||
if(!xdg_shell) {
|
if(!xdg_shell) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the XDG shell interface");
|
wlr_log(WLR_ERROR, "Unable to create the XDG shell interface");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
@ -324,6 +359,21 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
wlr_server_decoration_manager_set_default_mode(
|
wlr_server_decoration_manager_set_default_mode(
|
||||||
server_decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
|
server_decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
|
||||||
|
|
||||||
|
viewporter = wlr_viewporter_create(server.wl_display);
|
||||||
|
if(!viewporter) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to create the viewporter interface");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
presentation = wlr_presentation_create(server.wl_display, server.backend);
|
||||||
|
if(!presentation) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to create the presentation interface");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
wlr_scene_set_presentation(server.scene, presentation);
|
||||||
|
|
||||||
export_dmabuf_manager =
|
export_dmabuf_manager =
|
||||||
wlr_export_dmabuf_manager_v1_create(server.wl_display);
|
wlr_export_dmabuf_manager_v1_create(server.wl_display);
|
||||||
if(!export_dmabuf_manager) {
|
if(!export_dmabuf_manager) {
|
||||||
|
|
@ -347,17 +397,17 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
gamma_control_manager =
|
if(!wlr_primary_selection_v1_device_manager_create(server.wl_display)) {
|
||||||
wlr_gamma_control_manager_v1_create(server.wl_display);
|
wlr_log(WLR_ERROR,
|
||||||
if(!gamma_control_manager) {
|
"Unable to create the primary selection device manager");
|
||||||
wlr_log(WLR_ERROR, "Unable to create the gamma control manager");
|
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
compositor = wlr_compositor_create(server.wl_display, server.renderer);
|
gamma_control_manager =
|
||||||
if(!compositor) {
|
wlr_gamma_control_manager_v1_create(server.wl_display);
|
||||||
wlr_log(WLR_ERROR, "Unable to create the wlroots compositor");
|
if(!gamma_control_manager) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to create the gamma control manager");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
@ -372,13 +422,6 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
server.new_xwayland_surface.notify = handle_xwayland_surface_new;
|
server.new_xwayland_surface.notify = handle_xwayland_surface_new;
|
||||||
wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface);
|
wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface);
|
||||||
|
|
||||||
xcursor_manager = wlr_xcursor_manager_create(DEFAULT_XCURSOR, XCURSOR_SIZE);
|
|
||||||
if(!xcursor_manager) {
|
|
||||||
wlr_log(WLR_ERROR, "Cannot create XWayland XCursor manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(setenv("DISPLAY", xwayland->display_name, true) < 0) {
|
if(setenv("DISPLAY", xwayland->display_name, true) < 0) {
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to set DISPLAY for XWayland.",
|
wlr_log_errno(WLR_ERROR, "Unable to set DISPLAY for XWayland.",
|
||||||
"Clients may not be able to connect");
|
"Clients may not be able to connect");
|
||||||
|
|
@ -387,18 +430,15 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
xwayland->display_name);
|
xwayland->display_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(wlr_xcursor_manager_load(xcursor_manager, 1)) {
|
struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
|
||||||
wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme");
|
server.seat->xcursor_manager, DEFAULT_XCURSOR, 1);
|
||||||
}
|
|
||||||
struct wlr_xcursor *xcursor =
|
|
||||||
wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1);
|
|
||||||
if(xcursor) {
|
if(xcursor) {
|
||||||
struct wlr_xcursor_image *image = xcursor->images[0];
|
struct wlr_xcursor_image *image = xcursor->images[0];
|
||||||
wlr_xwayland_set_cursor(xwayland, image->buffer, image->width * 4,
|
wlr_xwayland_set_cursor(xwayland, image->buffer, image->width * 4,
|
||||||
image->width, image->height, image->hotspot_x,
|
image->width, image->height, image->hotspot_x,
|
||||||
image->hotspot_y);
|
image->hotspot_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char *socket = wl_display_add_socket_auto(server.wl_display);
|
const char *socket = wl_display_add_socket_auto(server.wl_display);
|
||||||
|
|
@ -418,42 +458,32 @@ LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.",
|
wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.",
|
||||||
"Clients may not be able to connect");
|
"Clients may not be able to connect");
|
||||||
} else {
|
} else {
|
||||||
wlr_log(WLR_DEBUG,
|
fprintf(stderr,
|
||||||
"Cagebreak " CG_VERSION " is running on Wayland display %s",
|
"Cagebreak " CG_VERSION " is running on Wayland display %s\n",
|
||||||
socket);
|
socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CG_HAS_XWAYLAND
|
||||||
|
wlr_xwayland_set_seat(xwayland, server.seat->seat);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Place the cursor to the top left of the output layout. */
|
||||||
|
wlr_cursor_warp(server.seat->cursor, NULL, 0, 0);
|
||||||
|
|
||||||
|
if(!drop_permissions()) {
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
/* Place the cursor to the topl left of the output layout. */
|
/* Place the cursor to the topl left of the output layout. */
|
||||||
wlr_cursor_warp(server.seat->cursor, NULL, 0, 0);
|
wlr_cursor_warp(server.seat->cursor, NULL, 0, 0);
|
||||||
atexit(cleanup);
|
atexit(cleanup);
|
||||||
// server.wl_display->run = 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
end:
|
end:
|
||||||
cleanup();
|
cleanup();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
move_cursor(char *line, struct cg_server *server) {
|
|
||||||
return; // TODO
|
|
||||||
long del = 0;
|
|
||||||
char *delstr = strtok_r(NULL, ";", &line);
|
|
||||||
enum wlr_axis_orientation orientation =
|
|
||||||
(*(line++) == '0') ? WLR_AXIS_ORIENTATION_VERTICAL
|
|
||||||
: WLR_AXIS_ORIENTATION_HORIZONTAL;
|
|
||||||
if(delstr == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
del = strtol(delstr, NULL, 10);
|
|
||||||
struct wlr_event_pointer_axis event = {.device = NULL,
|
|
||||||
.time_msec = 0,
|
|
||||||
.source = WLR_AXIS_SOURCE_WHEEL,
|
|
||||||
.orientation = orientation,
|
|
||||||
.delta = del * 15,
|
|
||||||
.delta_discrete = del};
|
|
||||||
// TODO dispatch_cursor_axis(server->seat->cursor, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
add_output_callback(struct wlr_backend *backend, void *data) {
|
add_output_callback(struct wlr_backend *backend, void *data) {
|
||||||
long *dims = data;
|
long *dims = data;
|
||||||
|
|
@ -485,73 +515,6 @@ create_output(char *line, struct cg_server *server) {
|
||||||
wlr_multi_for_each_backend(server->backend, add_output_callback, dims);
|
wlr_multi_for_each_backend(server->backend, add_output_callback, dims);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
add_input_device_callback(struct wlr_backend *backend, void *data) {
|
|
||||||
enum wlr_input_device_type *type = data;
|
|
||||||
wlr_headless_add_input_device(backend, *type);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
create_input_device(char *line, struct cg_server *server) {
|
|
||||||
enum wlr_input_device_type type;
|
|
||||||
if(*line != '\0') {
|
|
||||||
if(strncmp(line, "kbd", 3) == 0) {
|
|
||||||
type = WLR_INPUT_DEVICE_KEYBOARD;
|
|
||||||
wlr_multi_for_each_backend(server->backend,
|
|
||||||
add_input_device_callback, &type);
|
|
||||||
} else if(strncmp(line, "ptr", 3) == 0) {
|
|
||||||
type = WLR_INPUT_DEVICE_POINTER;
|
|
||||||
wlr_multi_for_each_backend(server->backend,
|
|
||||||
add_input_device_callback, &type);
|
|
||||||
} else if(strncmp(line, "tch", 3) == 0) {
|
|
||||||
type = WLR_INPUT_DEVICE_TOUCH;
|
|
||||||
wlr_multi_for_each_backend(server->backend,
|
|
||||||
add_input_device_callback, &type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
destroy_input_device(char *line, struct cg_server *server) {
|
|
||||||
long devn = 0;
|
|
||||||
if(line[0] != '\0') {
|
|
||||||
devn = strtol(line + 1, NULL, 10);
|
|
||||||
}
|
|
||||||
if(line != NULL) {
|
|
||||||
if(strncmp(line, "k", 1) == 0) {
|
|
||||||
if(wl_list_empty(&server->seat->keyboard_groups)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
devn = devn % wl_list_length(&server->seat->keyboard_groups);
|
|
||||||
struct cg_keyboard_group *group, *group_tmp;
|
|
||||||
wl_list_for_each_safe(group, group_tmp,
|
|
||||||
&server->seat->keyboard_groups, link) {
|
|
||||||
if(devn == 0) {
|
|
||||||
wl_list_remove(&group->link);
|
|
||||||
wlr_keyboard_group_destroy(group->wlr_group);
|
|
||||||
wl_event_source_remove(group->key_repeat_timer);
|
|
||||||
free(group);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
--devn;
|
|
||||||
}
|
|
||||||
} else if(strncmp(line, "p", 1) == 0) {
|
|
||||||
if(wl_list_empty(&server->input->devices)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
devn = devn % wl_list_length(&server->input->devices);
|
|
||||||
struct cg_input_device *dev, *dev_tmp;
|
|
||||||
wl_list_for_each_safe(dev, dev_tmp, &server->input->devices, link) {
|
|
||||||
if(devn == 0) {
|
|
||||||
dev->device_destroy.notify(&dev->device_destroy, NULL);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
--devn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
destroy_output(char *line, struct cg_server *server) {
|
destroy_output(char *line, struct cg_server *server) {
|
||||||
if(wl_list_length(&server->outputs) < 2) {
|
if(wl_list_length(&server->outputs) < 2) {
|
||||||
|
|
@ -571,5 +534,4 @@ destroy_output(char *line, struct cg_server *server) {
|
||||||
--outpn;
|
--outpn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it->damage_destroy.notify(&it->damage_destroy, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CG_FUZZ_LIB_H
|
#ifndef CG_FUZZ_LIB_H
|
||||||
#define CG_FUZZ_LIB_H
|
#define CG_FUZZ_LIB_H
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200812L
|
#define _POSIX_C_SOURCE 200812L
|
||||||
|
|
||||||
|
|
@ -38,7 +33,7 @@ set_configuration(struct cg_server *server, char *content) {
|
||||||
(line = strtok_r(NULL, "\n", &content)) != NULL; ++line_num) {
|
(line = strtok_r(NULL, "\n", &content)) != NULL; ++line_num) {
|
||||||
line[strcspn(line, "\n")] = '\0';
|
line[strcspn(line, "\n")] = '\0';
|
||||||
if(*line != '\0' && *line != '#') {
|
if(*line != '\0' && *line != '#') {
|
||||||
char *errstr;
|
char *errstr = NULL;
|
||||||
server->running = true;
|
server->running = true;
|
||||||
if(parse_rc_line(server, line, &errstr) != 0) {
|
if(parse_rc_line(server, line, &errstr) != 0) {
|
||||||
if(errstr != NULL) {
|
if(errstr != NULL) {
|
||||||
|
|
@ -56,10 +51,9 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
if(size == 0) {
|
if(size == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int max_line_size = 256 > size ? size : 256;
|
char *str = malloc(sizeof(char) * size);
|
||||||
char *str = malloc(sizeof(char) * max_line_size);
|
strncpy(str, (char *)data, size);
|
||||||
strncpy(str, (char *)data, max_line_size);
|
str[size - 1] = 0;
|
||||||
str[max_line_size - 1] = 0;
|
|
||||||
set_configuration(&server, str);
|
set_configuration(&server, str);
|
||||||
free(str);
|
free(str);
|
||||||
keybinding_list_free(server.keybindings);
|
keybinding_list_free(server.keybindings);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
# Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
# SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
fuzz_sources = [
|
fuzz_sources = [
|
||||||
'fuzz-parse.c',
|
'fuzz-parse.c',
|
||||||
'fuzz-lib.c',
|
'fuzz-lib.c',
|
||||||
|
|
@ -19,7 +22,7 @@ endif
|
||||||
|
|
||||||
override_lib = shared_library('execl_override',
|
override_lib = shared_library('execl_override',
|
||||||
[ 'execl_override.c' ],
|
[ 'execl_override.c' ],
|
||||||
dependencies: [ pixman,cairo,pango,pangocairo ],
|
dependencies: [ cairo,pango,pangocairo ],
|
||||||
install: false
|
install: false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2022 The Cagebreak Authors
|
|
||||||
* Copyright (C) 2018-2019 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/types/wlr_idle.h>
|
#include <wlr/types/wlr_idle.h>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
#ifndef CG_IDLE_INHIBIT_H
|
#ifndef CG_IDLE_INHIBIT_H
|
||||||
#define CG_IDLE_INHIBIT_H
|
#define CG_IDLE_INHIBIT_H
|
||||||
|
|
||||||
|
|
|
||||||
6
input.h
6
input.h
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef _CG_INPUT_H
|
#ifndef _CG_INPUT_H
|
||||||
#define _CG_INPUT_H
|
#define _CG_INPUT_H
|
||||||
|
|
||||||
|
|
@ -13,9 +16,6 @@ cg_input_configure_libinput_device(struct cg_input_device *device);
|
||||||
void
|
void
|
||||||
cg_input_apply_config(struct cg_input_config *config, struct cg_server *server);
|
cg_input_apply_config(struct cg_input_config *config, struct cg_server *server);
|
||||||
|
|
||||||
bool
|
|
||||||
cg_input_reset_libinput_device(struct cg_input_device *device);
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
cg_libinput_device_is_builtin(struct cg_input_device *device);
|
cg_libinput_device_is_builtin(struct cg_input_device *device);
|
||||||
|
|
||||||
|
|
|
||||||
303
input_manager.c
303
input_manager.c
|
|
@ -1,11 +1,5 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2022 The Cagebreak Authors
|
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200812L
|
#define _POSIX_C_SOURCE 200812L
|
||||||
|
|
||||||
|
|
@ -16,7 +10,9 @@
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <float.h>
|
||||||
#include <libevdev/libevdev.h>
|
#include <libevdev/libevdev.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -25,6 +21,7 @@
|
||||||
#include <wlr/types/wlr_cursor.h>
|
#include <wlr/types/wlr_cursor.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
#include <wlr/types/wlr_input_inhibitor.h>
|
#include <wlr/types/wlr_input_inhibitor.h>
|
||||||
|
#include <wlr/types/wlr_keyboard_group.h>
|
||||||
#include <wlr/types/wlr_virtual_keyboard_v1.h>
|
#include <wlr/types/wlr_virtual_keyboard_v1.h>
|
||||||
#include <wlr/types/wlr_virtual_pointer_v1.h>
|
#include <wlr/types/wlr_virtual_pointer_v1.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
@ -64,6 +61,7 @@ input_device_get_identifier(struct wlr_input_device *device) {
|
||||||
char *identifier = malloc(len);
|
char *identifier = malloc(len);
|
||||||
if(!identifier) {
|
if(!identifier) {
|
||||||
wlr_log(WLR_ERROR, "Unable to allocate unique input device name");
|
wlr_log(WLR_ERROR, "Unable to allocate unique input device name");
|
||||||
|
free(name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,13 +88,217 @@ input_manager_handle_device_destroy(struct wl_listener *listener, void *data) {
|
||||||
free(input_device);
|
free(input_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
struct cg_input_config *
|
||||||
handle_new_input(struct wl_listener *listener, void *data) {
|
input_manager_create_empty_input_config() {
|
||||||
struct cg_input_manager *input =
|
struct cg_input_config *cfg = calloc(1, sizeof(struct cg_input_config));
|
||||||
wl_container_of(listener, input, new_input);
|
if(cfg == NULL) {
|
||||||
struct cg_server *server = input->server;
|
return NULL;
|
||||||
struct wlr_input_device *device = data;
|
}
|
||||||
|
/* Libinput devices */
|
||||||
|
cfg->tap = INT_MIN;
|
||||||
|
cfg->tap_button_map = INT_MIN;
|
||||||
|
cfg->drag = INT_MIN;
|
||||||
|
cfg->drag_lock = INT_MIN;
|
||||||
|
cfg->dwt = INT_MIN;
|
||||||
|
cfg->send_events = INT_MIN;
|
||||||
|
cfg->click_method = INT_MIN;
|
||||||
|
cfg->middle_emulation = INT_MIN;
|
||||||
|
cfg->natural_scroll = INT_MIN;
|
||||||
|
cfg->accel_profile = INT_MIN;
|
||||||
|
cfg->pointer_accel = FLT_MIN;
|
||||||
|
cfg->scroll_factor = FLT_MIN;
|
||||||
|
cfg->scroll_button = INT_MIN;
|
||||||
|
cfg->scroll_method = INT_MIN;
|
||||||
|
cfg->left_handed = INT_MIN;
|
||||||
|
/*cfg->repeat_delay = INT_MIN;
|
||||||
|
cfg->repeat_rate = INT_MIN;
|
||||||
|
cfg->xkb_numlock = INT_MIN;
|
||||||
|
cfg->xkb_capslock = INT_MIN;
|
||||||
|
cfg->xkb_file_is_set = false;
|
||||||
|
wl_list_init(&cfg->tools);*/
|
||||||
|
|
||||||
|
/* Keyboards */
|
||||||
|
cfg->enable_keybindings = -1;
|
||||||
|
cfg->repeat_delay = -1;
|
||||||
|
cfg->repeat_rate = -1;
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cfg1 has precedence */
|
||||||
|
struct cg_input_config *
|
||||||
|
input_manager_merge_input_configs(struct cg_input_config *cfg1,
|
||||||
|
struct cg_input_config *cfg2) {
|
||||||
|
struct cg_input_config *out_cfg = calloc(1, sizeof(struct cg_input_config));
|
||||||
|
if(cfg1->identifier == NULL) {
|
||||||
|
if(cfg2->identifier != NULL) {
|
||||||
|
out_cfg->identifier = strdup(cfg2->identifier);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out_cfg->identifier = strdup(cfg1->identifier);
|
||||||
|
}
|
||||||
|
if(out_cfg == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(cfg1->tap == INT_MIN) {
|
||||||
|
out_cfg->tap = cfg2->tap;
|
||||||
|
} else {
|
||||||
|
out_cfg->tap = cfg1->tap;
|
||||||
|
}
|
||||||
|
if(cfg1->send_events == INT_MIN) {
|
||||||
|
out_cfg->send_events = cfg2->send_events;
|
||||||
|
} else {
|
||||||
|
out_cfg->send_events = cfg1->send_events;
|
||||||
|
}
|
||||||
|
if(cfg1->dwt == INT_MIN) {
|
||||||
|
out_cfg->dwt = cfg2->dwt;
|
||||||
|
} else {
|
||||||
|
out_cfg->dwt = cfg1->dwt;
|
||||||
|
}
|
||||||
|
if(cfg1->drag_lock == INT_MIN) {
|
||||||
|
out_cfg->drag_lock = cfg2->drag_lock;
|
||||||
|
} else {
|
||||||
|
out_cfg->drag_lock = cfg1->drag_lock;
|
||||||
|
}
|
||||||
|
if(cfg1->drag == INT_MIN) {
|
||||||
|
out_cfg->drag = cfg2->drag;
|
||||||
|
} else {
|
||||||
|
out_cfg->drag = cfg1->drag;
|
||||||
|
}
|
||||||
|
if(cfg1->tap_button_map == INT_MIN) {
|
||||||
|
out_cfg->tap_button_map = cfg2->tap_button_map;
|
||||||
|
} else {
|
||||||
|
out_cfg->tap_button_map = cfg1->tap_button_map;
|
||||||
|
}
|
||||||
|
if(cfg1->left_handed == INT_MIN) {
|
||||||
|
out_cfg->left_handed = cfg2->left_handed;
|
||||||
|
} else {
|
||||||
|
out_cfg->left_handed = cfg1->left_handed;
|
||||||
|
}
|
||||||
|
if(cfg1->scroll_method == INT_MIN) {
|
||||||
|
out_cfg->scroll_method = cfg2->scroll_method;
|
||||||
|
} else {
|
||||||
|
out_cfg->scroll_method = cfg1->scroll_method;
|
||||||
|
}
|
||||||
|
if(cfg1->scroll_button == INT_MIN) {
|
||||||
|
out_cfg->scroll_button = cfg2->scroll_button;
|
||||||
|
} else {
|
||||||
|
out_cfg->scroll_button = cfg1->scroll_button;
|
||||||
|
}
|
||||||
|
if(cfg1->scroll_factor == FLT_MIN) {
|
||||||
|
out_cfg->scroll_factor = cfg2->scroll_factor;
|
||||||
|
} else {
|
||||||
|
out_cfg->scroll_factor = cfg1->scroll_factor;
|
||||||
|
}
|
||||||
|
if(cfg1->pointer_accel == FLT_MIN) {
|
||||||
|
out_cfg->pointer_accel = cfg2->pointer_accel;
|
||||||
|
} else {
|
||||||
|
out_cfg->pointer_accel = cfg1->pointer_accel;
|
||||||
|
}
|
||||||
|
if(cfg1->accel_profile == INT_MIN) {
|
||||||
|
out_cfg->accel_profile = cfg2->accel_profile;
|
||||||
|
} else {
|
||||||
|
out_cfg->accel_profile = cfg1->accel_profile;
|
||||||
|
}
|
||||||
|
if(cfg1->natural_scroll == INT_MIN) {
|
||||||
|
out_cfg->natural_scroll = cfg2->natural_scroll;
|
||||||
|
} else {
|
||||||
|
out_cfg->natural_scroll = cfg1->natural_scroll;
|
||||||
|
}
|
||||||
|
if(cfg1->middle_emulation == INT_MIN) {
|
||||||
|
out_cfg->middle_emulation = cfg2->middle_emulation;
|
||||||
|
} else {
|
||||||
|
out_cfg->middle_emulation = cfg1->middle_emulation;
|
||||||
|
}
|
||||||
|
if(cfg1->click_method == INT_MIN) {
|
||||||
|
out_cfg->click_method = cfg2->click_method;
|
||||||
|
} else {
|
||||||
|
out_cfg->click_method = cfg1->click_method;
|
||||||
|
}
|
||||||
|
if(cfg1->enable_keybindings == -1) {
|
||||||
|
out_cfg->enable_keybindings = cfg2->enable_keybindings;
|
||||||
|
} else {
|
||||||
|
out_cfg->enable_keybindings = cfg1->enable_keybindings;
|
||||||
|
}
|
||||||
|
if(cfg1->repeat_delay == -1) {
|
||||||
|
out_cfg->repeat_delay = cfg2->repeat_delay;
|
||||||
|
} else {
|
||||||
|
out_cfg->repeat_delay = cfg1->repeat_delay;
|
||||||
|
}
|
||||||
|
if(cfg1->repeat_rate == -1) {
|
||||||
|
out_cfg->repeat_rate = cfg2->repeat_rate;
|
||||||
|
} else {
|
||||||
|
out_cfg->repeat_rate = cfg1->repeat_rate;
|
||||||
|
}
|
||||||
|
return out_cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
apply_keyboard_group_config(struct cg_input_config *config,
|
||||||
|
struct cg_keyboard_group *group) {
|
||||||
|
if(config->enable_keybindings != -1) {
|
||||||
|
group->enable_keybindings = config->enable_keybindings;
|
||||||
|
}
|
||||||
|
int repeat_delay = 600, repeat_rate = 25;
|
||||||
|
if(config->repeat_delay != -1) {
|
||||||
|
repeat_delay = config->repeat_delay;
|
||||||
|
}
|
||||||
|
if(config->repeat_rate != -1) {
|
||||||
|
repeat_rate = config->repeat_rate;
|
||||||
|
}
|
||||||
|
wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate,
|
||||||
|
repeat_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cg_input_manager_configure_keyboard_group(struct cg_keyboard_group *group) {
|
||||||
|
struct cg_server *server = group->seat->server;
|
||||||
|
struct cg_input_config *tot_cfg = input_manager_create_empty_input_config();
|
||||||
|
if(tot_cfg == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cg_input_config *tmp_cfg, *config = NULL;
|
||||||
|
wl_list_for_each(config, &server->input_config, link) {
|
||||||
|
if(strcmp(config->identifier, group->identifier) == 0 ||
|
||||||
|
strcmp(config->identifier, "*") == 0 ||
|
||||||
|
(strncmp(config->identifier, "type:", 5) == 0 &&
|
||||||
|
strcmp(config->identifier + 5, "keyboard") == 0)) {
|
||||||
|
tmp_cfg = tot_cfg;
|
||||||
|
if(tot_cfg->identifier == NULL ||
|
||||||
|
strcmp(tot_cfg->identifier, "*") == 0 ||
|
||||||
|
strcmp(config->identifier, group->identifier)) {
|
||||||
|
tot_cfg = input_manager_merge_input_configs(config, tot_cfg);
|
||||||
|
} else {
|
||||||
|
tot_cfg = input_manager_merge_input_configs(tot_cfg, config);
|
||||||
|
}
|
||||||
|
if(tmp_cfg->identifier != NULL) {
|
||||||
|
free(tmp_cfg->identifier);
|
||||||
|
}
|
||||||
|
free(tmp_cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apply_keyboard_group_config(tot_cfg, group);
|
||||||
|
if(tot_cfg->identifier != NULL) {
|
||||||
|
free(tot_cfg->identifier);
|
||||||
|
}
|
||||||
|
free(tot_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cg_input_manager_configure(struct cg_server *server) {
|
||||||
|
struct cg_input_device *device = NULL;
|
||||||
|
wl_list_for_each(device, &server->input->devices, link) {
|
||||||
|
cg_input_configure_libinput_device(device);
|
||||||
|
}
|
||||||
|
struct cg_keyboard_group *group = NULL;
|
||||||
|
wl_list_for_each(group, &server->seat->keyboard_groups, link) {
|
||||||
|
cg_input_manager_configure_keyboard_group(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
new_input(struct cg_input_manager *input, struct wlr_input_device *device,
|
||||||
|
bool virtual) {
|
||||||
struct cg_input_device *input_device =
|
struct cg_input_device *input_device =
|
||||||
calloc(1, sizeof(struct cg_input_device));
|
calloc(1, sizeof(struct cg_input_device));
|
||||||
if(!input_device) {
|
if(!input_device) {
|
||||||
|
|
@ -105,9 +307,10 @@ handle_new_input(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
device->data = input_device;
|
device->data = input_device;
|
||||||
|
|
||||||
|
input_device->is_virtual = virtual;
|
||||||
input_device->wlr_device = device;
|
input_device->wlr_device = device;
|
||||||
input_device->identifier = input_device_get_identifier(device);
|
input_device->identifier = input_device_get_identifier(device);
|
||||||
input_device->server = server;
|
input_device->server = input->server;
|
||||||
input_device->pointer = NULL;
|
input_device->pointer = NULL;
|
||||||
input_device->touch = NULL;
|
input_device->touch = NULL;
|
||||||
|
|
||||||
|
|
@ -118,39 +321,28 @@ handle_new_input(struct wl_listener *listener, void *data) {
|
||||||
wl_signal_add(&device->events.destroy, &input_device->device_destroy);
|
wl_signal_add(&device->events.destroy, &input_device->device_destroy);
|
||||||
input_device->device_destroy.notify = input_manager_handle_device_destroy;
|
input_device->device_destroy.notify = input_manager_handle_device_destroy;
|
||||||
|
|
||||||
struct cg_seat *seat = server->seat;
|
struct cg_seat *seat = input->server->seat;
|
||||||
seat_add_device(seat, input_device);
|
seat_add_device(seat, input_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_new_input(struct wl_listener *listener, void *data) {
|
||||||
|
struct cg_input_manager *input =
|
||||||
|
wl_container_of(listener, input, new_input);
|
||||||
|
struct wlr_input_device *device = data;
|
||||||
|
|
||||||
|
new_input(input, device, false);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
handle_virtual_keyboard(struct wl_listener *listener, void *data) {
|
handle_virtual_keyboard(struct wl_listener *listener, void *data) {
|
||||||
struct cg_input_manager *input_manager =
|
|
||||||
wl_container_of(listener, input_manager, virtual_keyboard_new);
|
struct cg_input_manager *input =
|
||||||
|
wl_container_of(listener, input, virtual_keyboard_new);
|
||||||
struct wlr_virtual_keyboard_v1 *keyboard = data;
|
struct wlr_virtual_keyboard_v1 *keyboard = data;
|
||||||
struct wlr_input_device *device = &keyboard->input_device;
|
struct wlr_input_device *device = &keyboard->keyboard.base;
|
||||||
|
|
||||||
struct cg_seat *seat = input_manager->server->seat;
|
new_input(input, device, true);
|
||||||
|
|
||||||
struct cg_input_device *input_device =
|
|
||||||
calloc(1, sizeof(struct cg_input_device));
|
|
||||||
if(!input_device) {
|
|
||||||
wlr_log(WLR_ERROR, "could not allocate input device");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
device->data = input_device;
|
|
||||||
|
|
||||||
input_device->is_virtual = true;
|
|
||||||
input_device->wlr_device = device;
|
|
||||||
input_device->identifier = input_device_get_identifier(device);
|
|
||||||
wl_list_insert(&input_manager->devices, &input_device->link);
|
|
||||||
input_device->server = input_manager->server;
|
|
||||||
input_device->pointer = NULL;
|
|
||||||
input_device->touch = NULL;
|
|
||||||
|
|
||||||
wl_signal_add(&device->events.destroy, &input_device->device_destroy);
|
|
||||||
input_device->device_destroy.notify = input_manager_handle_device_destroy;
|
|
||||||
|
|
||||||
seat_add_device(seat, input_device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -159,34 +351,13 @@ handle_virtual_pointer(struct wl_listener *listener, void *data) {
|
||||||
wl_container_of(listener, input_manager, virtual_pointer_new);
|
wl_container_of(listener, input_manager, virtual_pointer_new);
|
||||||
struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
|
struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
|
||||||
struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
|
struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
|
||||||
struct wlr_input_device *device = &pointer->input_device;
|
struct wlr_input_device *device = &pointer->pointer.base;
|
||||||
|
|
||||||
struct cg_seat *seat = input_manager->server->seat;
|
new_input(input_manager, device, true);
|
||||||
|
|
||||||
struct cg_input_device *input_device =
|
|
||||||
calloc(1, sizeof(struct cg_input_device));
|
|
||||||
if(!input_device) {
|
|
||||||
wlr_log(WLR_ERROR, "could not allocate input device");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
device->data = input_device;
|
|
||||||
|
|
||||||
input_device->is_virtual = true;
|
|
||||||
input_device->wlr_device = device;
|
|
||||||
input_device->identifier = input_device_get_identifier(device);
|
|
||||||
wl_list_insert(&input_manager->devices, &input_device->link);
|
|
||||||
input_device->server = input_manager->server;
|
|
||||||
input_device->pointer = NULL;
|
|
||||||
input_device->touch = NULL;
|
|
||||||
|
|
||||||
wl_signal_add(&device->events.destroy, &input_device->device_destroy);
|
|
||||||
input_device->device_destroy.notify = input_manager_handle_device_destroy;
|
|
||||||
|
|
||||||
seat_add_device(seat, input_device);
|
|
||||||
|
|
||||||
if(event->suggested_output) {
|
if(event->suggested_output) {
|
||||||
wlr_cursor_map_input_to_output(seat->cursor, device,
|
wlr_cursor_map_input_to_output(input_manager->server->seat->cursor,
|
||||||
event->suggested_output);
|
device, event->suggested_output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef CG_INPUT_MANAGER_H
|
#ifndef CG_INPUT_MANAGER_H
|
||||||
#define CG_INPUT_MANAGER_H
|
#define CG_INPUT_MANAGER_H
|
||||||
|
|
||||||
|
|
@ -8,12 +11,19 @@
|
||||||
|
|
||||||
struct cg_input_manager *
|
struct cg_input_manager *
|
||||||
input_manager_create(struct cg_server *server);
|
input_manager_create(struct cg_server *server);
|
||||||
struct cg_input_config *
|
|
||||||
input_device_get_config(struct cg_input_device *device);
|
|
||||||
void
|
void
|
||||||
input_manager_handle_device_destroy(struct wl_listener *listener, void *data);
|
input_manager_handle_device_destroy(struct wl_listener *listener, void *data);
|
||||||
uint32_t
|
uint32_t
|
||||||
input_manager_get_mouse_button(const char *name, char **error);
|
input_manager_get_mouse_button(const char *name, char **error);
|
||||||
|
struct cg_input_config *
|
||||||
|
input_manager_create_empty_input_config();
|
||||||
|
struct cg_input_config *
|
||||||
|
input_manager_merge_input_configs(struct cg_input_config *cfg1,
|
||||||
|
struct cg_input_config *cfg2);
|
||||||
|
void
|
||||||
|
cg_input_manager_configure(struct cg_server *server);
|
||||||
|
void
|
||||||
|
cg_input_manager_configure_keyboard_group(struct cg_keyboard_group *group);
|
||||||
|
|
||||||
struct cg_input_manager {
|
struct cg_input_manager {
|
||||||
struct wl_list devices;
|
struct wl_list devices;
|
||||||
|
|
@ -50,6 +60,7 @@ enum cg_input_config_mapped_to {
|
||||||
struct cg_input_config {
|
struct cg_input_config {
|
||||||
char *identifier;
|
char *identifier;
|
||||||
|
|
||||||
|
/* Libinput devices */
|
||||||
int accel_profile;
|
int accel_profile;
|
||||||
struct calibration_matrix calibration_matrix;
|
struct calibration_matrix calibration_matrix;
|
||||||
int click_method;
|
int click_method;
|
||||||
|
|
@ -92,13 +103,18 @@ struct cg_input_config {
|
||||||
|
|
||||||
bool capturable;
|
bool capturable;
|
||||||
struct wlr_box region;
|
struct wlr_box region;
|
||||||
|
|
||||||
|
/* Keyboards */
|
||||||
|
int enable_keybindings;
|
||||||
|
int repeat_delay;
|
||||||
|
int repeat_rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_input_device {
|
struct cg_input_device {
|
||||||
char *identifier;
|
char *identifier;
|
||||||
struct cg_server *server;
|
struct cg_server *server;
|
||||||
struct wlr_input_device *wlr_device;
|
struct wlr_input_device *wlr_device;
|
||||||
struct wl_list link;
|
struct wl_list link; // input_manager::devices
|
||||||
struct wl_listener device_destroy;
|
struct wl_listener device_destroy;
|
||||||
bool is_virtual;
|
bool is_virtual;
|
||||||
|
|
||||||
|
|
|
||||||
177
ipc_server.c
177
ipc_server.c
|
|
@ -1,27 +1,28 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2022 The Cagebreak Authors
|
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
|
||||||
#include "ipc_server.h"
|
#include "ipc_server.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
|
static const char ipc_magic[] = {'c', 'g', '-', 'i', 'p', 'c'};
|
||||||
|
|
||||||
|
#define IPC_HEADER_SIZE sizeof(ipc_magic)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_display_destroy(struct wl_listener *listener, void *data) {
|
handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
struct cg_ipc_handle *ipc = wl_container_of(listener, ipc, display_destroy);
|
struct cg_ipc_handle *ipc = wl_container_of(listener, ipc, display_destroy);
|
||||||
|
|
@ -43,6 +44,9 @@ handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
int
|
int
|
||||||
ipc_init(struct cg_server *server) {
|
ipc_init(struct cg_server *server) {
|
||||||
|
if(server->enable_socket == false) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
struct cg_ipc_handle *ipc = &server->ipc;
|
struct cg_ipc_handle *ipc = &server->ipc;
|
||||||
ipc->socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
ipc->socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
if(ipc->socket == -1) {
|
if(ipc->socket == -1) {
|
||||||
|
|
@ -96,6 +100,7 @@ ipc_init(struct cg_server *server) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chmod(ipc->sockaddr->sun_path, 0700);
|
||||||
setenv("CAGEBREAK_SOCKET", ipc->sockaddr->sun_path, 1);
|
setenv("CAGEBREAK_SOCKET", ipc->sockaddr->sun_path, 1);
|
||||||
|
|
||||||
wl_list_init(&ipc->client_list);
|
wl_list_init(&ipc->client_list);
|
||||||
|
|
@ -109,6 +114,50 @@ ipc_init(struct cg_server *server) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
|
||||||
|
struct cg_ipc_client *client = data;
|
||||||
|
|
||||||
|
if(mask & WL_EVENT_ERROR) {
|
||||||
|
ipc_client_disconnect(client);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mask & WL_EVENT_HANGUP) {
|
||||||
|
ipc_client_disconnect(client);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(client->write_buffer_len <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fcntl(client->fd, F_GETFD) == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ssize_t written =
|
||||||
|
write(client->fd, client->write_buffer, client->write_buffer_len);
|
||||||
|
|
||||||
|
if(written == -1 && errno == EAGAIN) {
|
||||||
|
return 0;
|
||||||
|
} else if(written == -1) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to send data from queue to IPC client");
|
||||||
|
ipc_client_disconnect(client);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memmove(client->write_buffer, client->write_buffer + written,
|
||||||
|
client->write_buffer_len - written);
|
||||||
|
client->write_buffer_len -= written;
|
||||||
|
|
||||||
|
if(client->write_buffer_len == 0 && client->writable_event_source) {
|
||||||
|
wl_event_source_remove(client->writable_event_source);
|
||||||
|
client->writable_event_source = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ipc_handle_connection(int fd, uint32_t mask, void *data) {
|
ipc_handle_connection(int fd, uint32_t mask, void *data) {
|
||||||
(void)fd;
|
(void)fd;
|
||||||
|
|
@ -145,8 +194,8 @@ ipc_handle_connection(int fd, uint32_t mask, void *data) {
|
||||||
close(client_fd);
|
close(client_fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// +1 for \n and +1 for \0
|
client->read_buf_cap = 64;
|
||||||
client->read_buffer = malloc(sizeof(char) * (MAX_LINE_SIZE + 2));
|
client->read_buffer = calloc(client->read_buf_cap, sizeof(char));
|
||||||
client->read_buf_len = 0;
|
client->read_buf_len = 0;
|
||||||
client->read_discard = 0;
|
client->read_discard = 0;
|
||||||
client->server = server;
|
client->server = server;
|
||||||
|
|
@ -154,7 +203,9 @@ ipc_handle_connection(int fd, uint32_t mask, void *data) {
|
||||||
client->event_source =
|
client->event_source =
|
||||||
wl_event_loop_add_fd(server->event_loop, client_fd, WL_EVENT_READABLE,
|
wl_event_loop_add_fd(server->event_loop, client_fd, WL_EVENT_READABLE,
|
||||||
ipc_client_handle_readable, client);
|
ipc_client_handle_readable, client);
|
||||||
client->writable_event_source = NULL;
|
client->writable_event_source =
|
||||||
|
wl_event_loop_add_fd(server->event_loop, client_fd, WL_EVENT_WRITABLE,
|
||||||
|
ipc_client_handle_writable, client);
|
||||||
|
|
||||||
client->write_buffer_size = 128;
|
client->write_buffer_size = 128;
|
||||||
client->write_buffer_len = 0;
|
client->write_buffer_len = 0;
|
||||||
|
|
@ -191,12 +242,22 @@ ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read_size = read_available < MAX_LINE_SIZE + 1 - client->read_buf_len
|
while(read_available + client->read_buf_len >
|
||||||
? read_available
|
(int32_t)client->read_buf_cap - 1) {
|
||||||
: MAX_LINE_SIZE + 1 - client->read_buf_len;
|
client->read_buf_cap *= 2;
|
||||||
|
client->read_buffer = reallocarray(client->read_buffer,
|
||||||
|
client->read_buf_cap, sizeof(char));
|
||||||
|
if(client->read_buffer == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to allocate buffer large enough to hold "
|
||||||
|
"client read data");
|
||||||
|
ipc_client_disconnect(client);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Append to buffer
|
// Append to buffer
|
||||||
ssize_t received = recv(
|
ssize_t received =
|
||||||
client_fd, client->read_buffer + client->read_buf_len, read_size, 0);
|
recv(client_fd, client->read_buffer + client->read_buf_len,
|
||||||
|
read_available, 0);
|
||||||
if(received == -1) {
|
if(received == -1) {
|
||||||
wlr_log(WLR_ERROR, "Unable to receive data from IPC client");
|
wlr_log(WLR_ERROR, "Unable to receive data from IPC client");
|
||||||
ipc_client_disconnect(client);
|
ipc_client_disconnect(client);
|
||||||
|
|
@ -224,8 +285,12 @@ ipc_client_disconnect(struct cg_ipc_client *client) {
|
||||||
wl_event_source_remove(client->writable_event_source);
|
wl_event_source_remove(client->writable_event_source);
|
||||||
}
|
}
|
||||||
wl_list_remove(&client->link);
|
wl_list_remove(&client->link);
|
||||||
free(client->write_buffer);
|
if(client->write_buffer != NULL) {
|
||||||
free(client->read_buffer);
|
free(client->write_buffer);
|
||||||
|
}
|
||||||
|
if(client->read_buffer != NULL) {
|
||||||
|
free(client->read_buffer);
|
||||||
|
}
|
||||||
close(client->fd);
|
close(client->fd);
|
||||||
free(client);
|
free(client);
|
||||||
}
|
}
|
||||||
|
|
@ -262,16 +327,78 @@ ipc_client_handle_command(struct cg_ipc_client *client) {
|
||||||
}
|
}
|
||||||
offset = (nl_pos - client->read_buffer) + 1;
|
offset = (nl_pos - client->read_buffer) + 1;
|
||||||
}
|
}
|
||||||
if(offset == 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Line received was longer that %d, discarding it",
|
|
||||||
MAX_LINE_SIZE);
|
|
||||||
client->read_buf_len = 0;
|
|
||||||
client->read_discard = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(offset < client->read_buf_len) {
|
if(offset < client->read_buf_len) {
|
||||||
memmove(client->read_buffer, client->read_buffer + offset,
|
memmove(client->read_buffer, client->read_buffer + offset,
|
||||||
client->read_buf_len - offset);
|
client->read_buf_len - offset);
|
||||||
}
|
}
|
||||||
client->read_buf_len -= offset;
|
client->read_buf_len -= offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ipc_send_event_client(struct cg_ipc_client *client, const char *payload,
|
||||||
|
uint32_t payload_length) {
|
||||||
|
char data[IPC_HEADER_SIZE];
|
||||||
|
|
||||||
|
memcpy(data, ipc_magic, sizeof(ipc_magic));
|
||||||
|
|
||||||
|
// +1 for terminating null character
|
||||||
|
while(client->write_buffer_len + IPC_HEADER_SIZE + payload_length + 1 >=
|
||||||
|
client->write_buffer_size) {
|
||||||
|
client->write_buffer_size *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(client->write_buffer_size > 4e6) { // 4 MB
|
||||||
|
wlr_log(WLR_ERROR,
|
||||||
|
"Client write buffer too big (%zu), disconnecting client",
|
||||||
|
client->write_buffer_size);
|
||||||
|
ipc_client_disconnect(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *new_buffer = realloc(client->write_buffer, client->write_buffer_size);
|
||||||
|
if(!new_buffer) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to reallocate ipc client write buffer");
|
||||||
|
ipc_client_disconnect(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
client->write_buffer = new_buffer;
|
||||||
|
|
||||||
|
memcpy(client->write_buffer + client->write_buffer_len, data,
|
||||||
|
IPC_HEADER_SIZE);
|
||||||
|
client->write_buffer_len += IPC_HEADER_SIZE;
|
||||||
|
memcpy(client->write_buffer + client->write_buffer_len, payload,
|
||||||
|
payload_length);
|
||||||
|
client->write_buffer_len += payload_length;
|
||||||
|
memcpy(client->write_buffer + client->write_buffer_len, "\0", 1);
|
||||||
|
client->write_buffer_len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ipc_send_event(struct cg_server *server, const char *fmt, ...) {
|
||||||
|
if(server->enable_socket == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(wl_list_empty(&server->ipc.client_list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
char *msg = malloc_vsprintf_va_list(fmt, args);
|
||||||
|
if(msg == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to allocate memory for ipc event");
|
||||||
|
va_end(args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
struct cg_ipc_client *it, *tmp;
|
||||||
|
uint32_t len = strlen(msg);
|
||||||
|
wl_list_for_each_safe(it, tmp, &server->ipc.client_list, link) {
|
||||||
|
if(it->writable_event_source == NULL) {
|
||||||
|
it->writable_event_source = wl_event_loop_add_fd(
|
||||||
|
server->event_loop, it->fd, WL_EVENT_WRITABLE,
|
||||||
|
ipc_client_handle_writable, it);
|
||||||
|
}
|
||||||
|
ipc_send_event_client(it, msg, len);
|
||||||
|
}
|
||||||
|
free(msg);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef CG_IPC_SERVER_H
|
#ifndef CG_IPC_SERVER_H
|
||||||
#define CG_IPC_SERVER_H
|
#define CG_IPC_SERVER_H
|
||||||
|
|
||||||
|
|
@ -21,6 +24,7 @@ struct cg_ipc_client {
|
||||||
char *write_buffer;
|
char *write_buffer;
|
||||||
// The following is for storing data between event_loop calls
|
// The following is for storing data between event_loop calls
|
||||||
uint16_t read_buf_len;
|
uint16_t read_buf_len;
|
||||||
|
size_t read_buf_cap;
|
||||||
uint8_t read_discard; // 1 if the current line is to be discarded
|
uint8_t read_discard; // 1 if the current line is to be discarded
|
||||||
char *read_buffer;
|
char *read_buffer;
|
||||||
};
|
};
|
||||||
|
|
@ -33,6 +37,8 @@ struct cg_ipc_handle {
|
||||||
struct sockaddr_un *sockaddr;
|
struct sockaddr_un *sockaddr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
ipc_send_event(struct cg_server *server, const char *fmt, ...);
|
||||||
int
|
int
|
||||||
ipc_init(struct cg_server *server);
|
ipc_init(struct cg_server *server);
|
||||||
int
|
int
|
||||||
|
|
|
||||||
934
keybinding.c
934
keybinding.c
File diff suppressed because it is too large
Load diff
130
keybinding.h
130
keybinding.h
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef KEYBINDING_H
|
#ifndef KEYBINDING_H
|
||||||
|
|
||||||
#define KEYBINDING_H
|
#define KEYBINDING_H
|
||||||
|
|
@ -10,57 +13,87 @@
|
||||||
|
|
||||||
struct cg_server;
|
struct cg_server;
|
||||||
|
|
||||||
|
#define FOREACH_KEYBINDING(KEYBINDING) \
|
||||||
|
KEYBINDING(KEYBINDING_RUN_COMMAND, \
|
||||||
|
exec) /* data.c is the string to execute */ \
|
||||||
|
KEYBINDING(KEYBINDING_CLOSE_VIEW, close) \
|
||||||
|
KEYBINDING(KEYBINDING_SPLIT_VERTICAL, vsplit) \
|
||||||
|
KEYBINDING(KEYBINDING_SPLIT_HORIZONTAL, hsplit) \
|
||||||
|
KEYBINDING(KEYBINDING_CHANGE_TTY, switchvt) /*data.u is the desired tty*/ \
|
||||||
|
KEYBINDING(KEYBINDING_LAYOUT_FULLSCREEN, only) \
|
||||||
|
KEYBINDING(KEYBINDING_CYCLE_VIEWS, \
|
||||||
|
cycle_views) /* data.b is 0 if forward, 1 if reverse */ \
|
||||||
|
KEYBINDING(KEYBINDING_CYCLE_TILES, \
|
||||||
|
cycle_tiles) /* data.b is 0 if forward, 1 if reverse */ \
|
||||||
|
KEYBINDING(KEYBINDING_CYCLE_OUTPUT, \
|
||||||
|
cycle_outputs) /* data.b is 0 if forward, 1 if reverse */ \
|
||||||
|
KEYBINDING(KEYBINDING_CONFIGURE_OUTPUT, \
|
||||||
|
output) /* data.o_cfg is the desired output */ \
|
||||||
|
\
|
||||||
|
KEYBINDING(KEYBINDING_CONFIGURE_MESSAGE, \
|
||||||
|
configure_message) /* data.m_cfg is the desired config */ \
|
||||||
|
KEYBINDING(KEYBINDING_CONFIGURE_INPUT, \
|
||||||
|
input) /* data.i_cfg is the desired input configuration */ \
|
||||||
|
\
|
||||||
|
KEYBINDING(KEYBINDING_QUIT, quit) \
|
||||||
|
KEYBINDING(KEYBINDING_NOOP, abort) \
|
||||||
|
KEYBINDING(KEYBINDING_SWITCH_OUTPUT, \
|
||||||
|
screen) /* data.u is the desired output */ \
|
||||||
|
KEYBINDING(KEYBINDING_SWITCH_WORKSPACE, \
|
||||||
|
workspace) /* data.u is the desired workspace */ \
|
||||||
|
KEYBINDING(KEYBINDING_SWITCH_MODE, mode) /* data.u is the desired mode */ \
|
||||||
|
KEYBINDING(KEYBINDING_SWITCH_DEFAULT_MODE, \
|
||||||
|
setmode) /* data.u is the desired mode */ \
|
||||||
|
KEYBINDING( \
|
||||||
|
KEYBINDING_RESIZE_TILE_HORIZONTAL, \
|
||||||
|
resize_tile_horizontal) /* data.i is the number of pixels to add */ \
|
||||||
|
\
|
||||||
|
KEYBINDING( \
|
||||||
|
KEYBINDING_RESIZE_TILE_VERTICAL, \
|
||||||
|
resize_tile_vertical) /* data.i is the number of pixels to add to */ \
|
||||||
|
\
|
||||||
|
KEYBINDING(KEYBINDING_MOVE_VIEW_TO_WORKSPACE, \
|
||||||
|
movetoworkspace) /* data.u is the desired workspace */ \
|
||||||
|
KEYBINDING(KEYBINDING_MOVE_VIEW_TO_OUTPUT, \
|
||||||
|
movetoscreen) /* data.u is the desired output */ \
|
||||||
|
KEYBINDING(KEYBINDING_MOVE_VIEW_TO_CYCLE_OUTPUT, \
|
||||||
|
move_view_to_cycle_output) /* data.b is 0 if forward, 1 if */ \
|
||||||
|
\
|
||||||
|
KEYBINDING(KEYBINDING_DUMP, dump) \
|
||||||
|
KEYBINDING(KEYBINDING_SHOW_TIME, time) \
|
||||||
|
KEYBINDING(KEYBINDING_SHOW_INFO, show_info) \
|
||||||
|
KEYBINDING(KEYBINDING_DISPLAY_MESSAGE, message) \
|
||||||
|
KEYBINDING(KEYBINDING_CURSOR, cursor) \
|
||||||
|
\
|
||||||
|
KEYBINDING(KEYBINDING_SWAP_LEFT, exchangeleft) \
|
||||||
|
KEYBINDING(KEYBINDING_SWAP_RIGHT, exchangeright) \
|
||||||
|
KEYBINDING(KEYBINDING_SWAP_TOP, exchangeup) \
|
||||||
|
KEYBINDING(KEYBINDING_SWAP_BOTTOM, exchangedown) \
|
||||||
|
\
|
||||||
|
KEYBINDING(KEYBINDING_FOCUS_LEFT, focusleft) \
|
||||||
|
KEYBINDING(KEYBINDING_FOCUS_RIGHT, focusright) \
|
||||||
|
KEYBINDING(KEYBINDING_FOCUS_TOP, focusup) \
|
||||||
|
KEYBINDING(KEYBINDING_FOCUS_BOTTOM, focusdown) \
|
||||||
|
KEYBINDING(KEYBINDING_FOCUS_TILE, focus_tile) \
|
||||||
|
\
|
||||||
|
KEYBINDING(KEYBINDING_DEFINEKEY, \
|
||||||
|
definekey) /* data.kb is the keybinding definition */ \
|
||||||
|
KEYBINDING(KEYBINDING_BACKGROUND, \
|
||||||
|
background) /* data.color is the background color */ \
|
||||||
|
KEYBINDING(KEYBINDING_DEFINEMODE, \
|
||||||
|
definemode) /* data.c is the mode name */ \
|
||||||
|
KEYBINDING(KEYBINDING_WORKSPACES, \
|
||||||
|
workspaces) /* data.i is the number of workspaces */
|
||||||
|
|
||||||
|
#define GENERATE_ENUM(ENUM, NAME) ENUM,
|
||||||
|
#define GENERATE_STRING(STRING, NAME) #NAME,
|
||||||
|
|
||||||
/* Important: if you add a keybinding which uses data.c or requires "free"
|
/* Important: if you add a keybinding which uses data.c or requires "free"
|
||||||
* to be called, don't forget to add it to the function "keybinding_list_free"
|
* to be called, don't forget to add it to the function "keybinding_list_free"
|
||||||
* in keybinding.c */
|
* in keybinding.c */
|
||||||
enum keybinding_action {
|
enum keybinding_action { FOREACH_KEYBINDING(GENERATE_ENUM) };
|
||||||
KEYBINDING_RUN_COMMAND, // data.c is the string to execute
|
|
||||||
KEYBINDING_CLOSE_VIEW,
|
|
||||||
KEYBINDING_SPLIT_VERTICAL,
|
|
||||||
KEYBINDING_SPLIT_HORIZONTAL,
|
|
||||||
KEYBINDING_CHANGE_TTY, // data.u is the desired tty
|
|
||||||
KEYBINDING_LAYOUT_FULLSCREEN,
|
|
||||||
KEYBINDING_CYCLE_VIEWS, // data.b is 0 if forward, 1 if reverse
|
|
||||||
KEYBINDING_CYCLE_TILES, // data.b is 0 if forward, 1 if reverse
|
|
||||||
KEYBINDING_CYCLE_OUTPUT, // data.b is 0 if forward, 1 if reverse
|
|
||||||
KEYBINDING_CONFIGURE_OUTPUT, // data.o_cfg is the desired output
|
|
||||||
// configuration
|
|
||||||
KEYBINDING_CONFIGURE_MESSAGE, // data.m_cfg is the desired config
|
|
||||||
KEYBINDING_CONFIGURE_INPUT, // data.i_cfg is the desired input
|
|
||||||
// configuration
|
|
||||||
KEYBINDING_QUIT,
|
|
||||||
KEYBINDING_NOOP,
|
|
||||||
KEYBINDING_SWITCH_OUTPUT, // data.u is the desired output
|
|
||||||
KEYBINDING_SWITCH_WORKSPACE, // data.u is the desired workspace
|
|
||||||
KEYBINDING_SWITCH_MODE, // data.u is the desired mode
|
|
||||||
KEYBINDING_SWITCH_DEFAULT_MODE, // data.u is the desired mode
|
|
||||||
KEYBINDING_RESIZE_TILE_HORIZONTAL, // data.i is the number of pixels to add
|
|
||||||
// to the current width
|
|
||||||
KEYBINDING_RESIZE_TILE_VERTICAL, // data.i is the number of pixels to add to
|
|
||||||
// the current height
|
|
||||||
KEYBINDING_MOVE_VIEW_TO_WORKSPACE, // data.u is the desired workspace
|
|
||||||
KEYBINDING_MOVE_VIEW_TO_OUTPUT, // data.u is the desired output
|
|
||||||
KEYBINDING_MOVE_VIEW_TO_CYCLE_OUTPUT, // data.b is 0 if forward, 1 if
|
|
||||||
// reverse
|
|
||||||
KEYBINDING_SHOW_TIME,
|
|
||||||
KEYBINDING_SHOW_INFO,
|
|
||||||
KEYBINDING_DISPLAY_MESSAGE,
|
|
||||||
|
|
||||||
KEYBINDING_SWAP_LEFT,
|
extern char *keybinding_action_string[];
|
||||||
KEYBINDING_SWAP_RIGHT,
|
|
||||||
KEYBINDING_SWAP_TOP,
|
|
||||||
KEYBINDING_SWAP_BOTTOM,
|
|
||||||
|
|
||||||
KEYBINDING_FOCUS_LEFT,
|
|
||||||
KEYBINDING_FOCUS_RIGHT,
|
|
||||||
KEYBINDING_FOCUS_TOP,
|
|
||||||
KEYBINDING_FOCUS_BOTTOM,
|
|
||||||
|
|
||||||
KEYBINDING_DEFINEKEY, // data.kb is the keybinding definition
|
|
||||||
KEYBINDING_BACKGROUND, // data.color is the background color
|
|
||||||
KEYBINDING_DEFINEMODE, // data.c is the mode name
|
|
||||||
KEYBINDING_WORKSPACES, // data.i is the number of workspaces
|
|
||||||
};
|
|
||||||
|
|
||||||
union keybinding_params {
|
union keybinding_params {
|
||||||
char *c;
|
char *c;
|
||||||
|
|
@ -94,7 +127,8 @@ keybinding_list_push(struct keybinding_list *list,
|
||||||
void
|
void
|
||||||
keybinding_list_free(struct keybinding_list *list);
|
keybinding_list_free(struct keybinding_list *list);
|
||||||
void
|
void
|
||||||
keybinding_cycle_outputs(struct cg_server *server, bool reverse);
|
keybinding_cycle_outputs(struct cg_server *server, bool reverse,
|
||||||
|
bool trigger_event);
|
||||||
struct keybinding **
|
struct keybinding **
|
||||||
find_keybinding(const struct keybinding_list *list,
|
find_keybinding(const struct keybinding_list *list,
|
||||||
const struct keybinding *keybinding);
|
const struct keybinding *keybinding);
|
||||||
|
|
|
||||||
207
libinput.c
207
libinput.c
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
|
|
@ -262,89 +265,70 @@ input_device_get_type(struct cg_input_device *device) {
|
||||||
void
|
void
|
||||||
apply_config_to_device(struct cg_input_config *config,
|
apply_config_to_device(struct cg_input_config *config,
|
||||||
struct cg_input_device *input_device) {
|
struct cg_input_device *input_device) {
|
||||||
if(!wlr_input_device_is_libinput(input_device->wlr_device)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct libinput_device *device =
|
if(wlr_input_device_is_libinput(input_device->wlr_device)) {
|
||||||
wlr_libinput_get_device_handle(input_device->wlr_device);
|
struct libinput_device *device =
|
||||||
if(config->mapped_to_output &&
|
wlr_libinput_get_device_handle(input_device->wlr_device);
|
||||||
!output_by_name_or_id(config->mapped_to_output, input_device->server)) {
|
if(config->mapped_to_output &&
|
||||||
wlr_log(WLR_DEBUG,
|
!output_by_name_or_id(config->mapped_to_output,
|
||||||
"'%s' is mapped to offline output '%s'; disabling input",
|
input_device->server)) {
|
||||||
config->identifier, config->mapped_to_output);
|
wlr_log(WLR_DEBUG,
|
||||||
set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
|
"'%s' is mapped to offline output '%s'; disabling input",
|
||||||
} else if(config->send_events != INT_MIN) {
|
config->identifier, config->mapped_to_output);
|
||||||
set_send_events(device, config->send_events);
|
set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
|
||||||
} else {
|
} else if(config->send_events != INT_MIN) {
|
||||||
// Have to reset to the default mode here, otherwise if ic->send_events
|
set_send_events(device, config->send_events);
|
||||||
// is unset and a mapped output just came online after being disabled,
|
} else {
|
||||||
// we'd remain stuck sending no events.
|
// Have to reset to the default mode here, otherwise if
|
||||||
set_send_events(
|
// ic->send_events is unset and a mapped output just came online
|
||||||
device,
|
// after being disabled, we'd remain stuck sending no events.
|
||||||
libinput_device_config_send_events_get_default_mode(device));
|
set_send_events(
|
||||||
}
|
device,
|
||||||
|
libinput_device_config_send_events_get_default_mode(device));
|
||||||
if(config->tap != INT_MIN) {
|
|
||||||
set_tap(device, config->tap);
|
|
||||||
}
|
|
||||||
if(config->tap_button_map != INT_MIN) {
|
|
||||||
set_tap_button_map(device, config->tap_button_map);
|
|
||||||
}
|
|
||||||
if(config->drag != INT_MIN) {
|
|
||||||
set_tap_drag(device, config->drag);
|
|
||||||
}
|
|
||||||
if(config->drag_lock != INT_MIN) {
|
|
||||||
set_tap_drag_lock(device, config->drag_lock);
|
|
||||||
}
|
|
||||||
if(config->pointer_accel != FLT_MIN) {
|
|
||||||
set_accel_speed(device, config->pointer_accel);
|
|
||||||
}
|
|
||||||
if(config->accel_profile != INT_MIN) {
|
|
||||||
set_accel_profile(device, config->accel_profile);
|
|
||||||
}
|
|
||||||
if(config->natural_scroll != INT_MIN) {
|
|
||||||
set_natural_scroll(device, config->natural_scroll);
|
|
||||||
}
|
|
||||||
if(config->left_handed != INT_MIN) {
|
|
||||||
set_left_handed(device, config->left_handed);
|
|
||||||
}
|
|
||||||
if(config->click_method != INT_MIN) {
|
|
||||||
set_click_method(device, config->click_method);
|
|
||||||
}
|
|
||||||
if(config->middle_emulation != INT_MIN) {
|
|
||||||
set_middle_emulation(device, config->middle_emulation);
|
|
||||||
}
|
|
||||||
if(config->scroll_method != INT_MIN) {
|
|
||||||
set_scroll_method(device, config->scroll_method);
|
|
||||||
}
|
|
||||||
if(config->scroll_button != INT_MIN) {
|
|
||||||
set_scroll_button(device, config->scroll_button);
|
|
||||||
}
|
|
||||||
if(config->dwt != INT_MIN) {
|
|
||||||
set_dwt(device, config->dwt);
|
|
||||||
}
|
|
||||||
if(config->calibration_matrix.configured) {
|
|
||||||
set_calibration_matrix(device, config->calibration_matrix.matrix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cg_input_apply_config(struct cg_input_config *config,
|
|
||||||
struct cg_server *server) {
|
|
||||||
struct cg_input_device *device = NULL;
|
|
||||||
wl_list_for_each(device, &server->input->devices, link) {
|
|
||||||
if(strcmp(config->identifier, device->identifier) != 0 &&
|
|
||||||
strcmp(config->identifier, "*") != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *device_type = input_device_get_type(device);
|
if(config->tap != INT_MIN) {
|
||||||
if(strncmp(config->identifier, "type:", 5) == 0 &&
|
set_tap(device, config->tap);
|
||||||
strcmp(config->identifier + 5, device_type) != 0) {
|
}
|
||||||
continue;
|
if(config->tap_button_map != INT_MIN) {
|
||||||
|
set_tap_button_map(device, config->tap_button_map);
|
||||||
|
}
|
||||||
|
if(config->drag != INT_MIN) {
|
||||||
|
set_tap_drag(device, config->drag);
|
||||||
|
}
|
||||||
|
if(config->drag_lock != INT_MIN) {
|
||||||
|
set_tap_drag_lock(device, config->drag_lock);
|
||||||
|
}
|
||||||
|
if(config->pointer_accel != FLT_MIN) {
|
||||||
|
set_accel_speed(device, config->pointer_accel);
|
||||||
|
}
|
||||||
|
if(config->accel_profile != INT_MIN) {
|
||||||
|
set_accel_profile(device, config->accel_profile);
|
||||||
|
}
|
||||||
|
if(config->natural_scroll != INT_MIN) {
|
||||||
|
set_natural_scroll(device, config->natural_scroll);
|
||||||
|
}
|
||||||
|
if(config->left_handed != INT_MIN) {
|
||||||
|
set_left_handed(device, config->left_handed);
|
||||||
|
}
|
||||||
|
if(config->click_method != INT_MIN) {
|
||||||
|
set_click_method(device, config->click_method);
|
||||||
|
}
|
||||||
|
if(config->middle_emulation != INT_MIN) {
|
||||||
|
set_middle_emulation(device, config->middle_emulation);
|
||||||
|
}
|
||||||
|
if(config->scroll_method != INT_MIN) {
|
||||||
|
set_scroll_method(device, config->scroll_method);
|
||||||
|
}
|
||||||
|
if(config->scroll_button != INT_MIN) {
|
||||||
|
set_scroll_button(device, config->scroll_button);
|
||||||
|
}
|
||||||
|
if(config->dwt != INT_MIN) {
|
||||||
|
set_dwt(device, config->dwt);
|
||||||
|
}
|
||||||
|
if(config->calibration_matrix.configured) {
|
||||||
|
set_calibration_matrix(device, config->calibration_matrix.matrix);
|
||||||
}
|
}
|
||||||
apply_config_to_device(config, device);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,71 +338,16 @@ cg_input_configure_libinput_device(struct cg_input_device *input_device) {
|
||||||
struct cg_input_config *config = NULL;
|
struct cg_input_config *config = NULL;
|
||||||
|
|
||||||
wl_list_for_each(config, &server->input_config, link) {
|
wl_list_for_each(config, &server->input_config, link) {
|
||||||
if(strcmp(config->identifier, input_device->identifier) != 0 &&
|
|
||||||
strcmp(config->identifier, "*") != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *device_type = input_device_get_type(input_device);
|
const char *device_type = input_device_get_type(input_device);
|
||||||
if(!(strncmp(config->identifier, "type:", 5) &&
|
if(strcmp(config->identifier, input_device->identifier) == 0 ||
|
||||||
strcmp(config->identifier + 5, device_type) == 0)) {
|
strcmp(config->identifier, "*") == 0 ||
|
||||||
continue;
|
(strncmp(config->identifier, "type:", 5) == 0 &&
|
||||||
|
strcmp(config->identifier + 5, device_type) == 0)) {
|
||||||
|
apply_config_to_device(config, input_device);
|
||||||
}
|
}
|
||||||
apply_config_to_device(config, input_device);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
cg_input_reset_libinput_device(struct cg_input_device *input_device) {
|
|
||||||
if(!wlr_input_device_is_libinput(input_device->wlr_device)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct libinput_device *device =
|
|
||||||
wlr_libinput_get_device_handle(input_device->wlr_device);
|
|
||||||
wlr_log(WLR_DEBUG, "cg_input_reset_libinput_device(%s)",
|
|
||||||
input_device->identifier);
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
changed |= set_send_events(
|
|
||||||
device, libinput_device_config_send_events_get_default_mode(device));
|
|
||||||
changed |=
|
|
||||||
set_tap(device, libinput_device_config_tap_get_default_enabled(device));
|
|
||||||
changed |= set_tap_button_map(
|
|
||||||
device, libinput_device_config_tap_get_default_button_map(device));
|
|
||||||
changed |= set_tap_drag(
|
|
||||||
device, libinput_device_config_tap_get_default_drag_enabled(device));
|
|
||||||
changed |= set_tap_drag_lock(
|
|
||||||
device,
|
|
||||||
libinput_device_config_tap_get_default_drag_lock_enabled(device));
|
|
||||||
changed |= set_accel_speed(
|
|
||||||
device, libinput_device_config_accel_get_default_speed(device));
|
|
||||||
changed |= set_accel_profile(
|
|
||||||
device, libinput_device_config_accel_get_default_profile(device));
|
|
||||||
changed |= set_natural_scroll(
|
|
||||||
device,
|
|
||||||
libinput_device_config_scroll_get_default_natural_scroll_enabled(
|
|
||||||
device));
|
|
||||||
changed |= set_left_handed(
|
|
||||||
device, libinput_device_config_left_handed_get_default(device));
|
|
||||||
changed |= set_click_method(
|
|
||||||
device, libinput_device_config_click_get_default_method(device));
|
|
||||||
changed |= set_middle_emulation(
|
|
||||||
device,
|
|
||||||
libinput_device_config_middle_emulation_get_default_enabled(device));
|
|
||||||
changed |= set_scroll_method(
|
|
||||||
device, libinput_device_config_scroll_get_default_method(device));
|
|
||||||
changed |= set_scroll_button(
|
|
||||||
device, libinput_device_config_scroll_get_default_button(device));
|
|
||||||
changed |=
|
|
||||||
set_dwt(device, libinput_device_config_dwt_get_default_enabled(device));
|
|
||||||
|
|
||||||
float matrix[6];
|
|
||||||
libinput_device_config_calibration_get_default_matrix(device, matrix);
|
|
||||||
changed |= set_calibration_matrix(device, matrix);
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
cg_libinput_device_is_builtin(struct cg_input_device *cg_device) {
|
cg_libinput_device_is_builtin(struct cg_input_device *cg_device) {
|
||||||
if(!wlr_input_device_is_libinput(cg_device->wlr_device)) {
|
if(!wlr_input_device_is_libinput(cg_device->wlr_device)) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cagebreak-config(5) "Version 1.9.1" "Cagebreak Manual"
|
cagebreak-config(5) "Version 2.0.0" "Cagebreak Manual"
|
||||||
|
|
||||||
# NAME
|
# NAME
|
||||||
|
|
||||||
|
|
@ -17,6 +17,10 @@ are parsed sequentially but independently from the rest of the file.
|
||||||
|
|
||||||
Each line starting with a "#" is a comment.
|
Each line starting with a "#" is a comment.
|
||||||
|
|
||||||
|
Note that nesting of commands is limited to 50 times, though lines
|
||||||
|
are arbitrarily long in practice, though a reasonable limit of 4Mb has
|
||||||
|
been set.
|
||||||
|
|
||||||
See *KEY DEFINITIONS* for details on modifier keys and *MODES* for details
|
See *KEY DEFINITIONS* for details on modifier keys and *MODES* for details
|
||||||
on modes.
|
on modes.
|
||||||
|
|
||||||
|
|
@ -48,7 +52,7 @@ definekey root <key> <command>
|
||||||
Close current window - This may be useful for windows of
|
Close current window - This may be useful for windows of
|
||||||
applications which do not offer any method of closing them.
|
applications which do not offer any method of closing them.
|
||||||
|
|
||||||
configure_message [font <font description>|[f|b]g_color <r> <g> b> <a>|display_time <n>]
|
*configure_message [font <font description>|[f|b]g_color <r> <g> b> <a>|display_time <n>]*
|
||||||
Configure message characteristics -
|
Configure message characteristics -
|
||||||
- font <font description> sets
|
- font <font description> sets
|
||||||
- <font description> is
|
- <font description> is
|
||||||
|
|
@ -75,6 +79,11 @@ configure_message bg_color 1.0 0.0 0.0 1.0
|
||||||
configure_message display_time 4
|
configure_message display_time 4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*cursor [enable|disable]*
|
||||||
|
Enable or disable cursor
|
||||||
|
This simply hides the cursor. Pointing and clicking is
|
||||||
|
still possible.
|
||||||
|
|
||||||
*definekey <mode> <key> <command>*
|
*definekey <mode> <key> <command>*
|
||||||
Bind <key> to execute <command> if pressed in <mode> -
|
Bind <key> to execute <command> if pressed in <mode> -
|
||||||
*definekey* is a more general version of *bind*.
|
*definekey* is a more general version of *bind*.
|
||||||
|
|
@ -89,6 +98,9 @@ definemode foo
|
||||||
definekey foo C-t abort
|
definekey foo C-t abort
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*dump*
|
||||||
|
Triggers the *dump* event, see *cagebreak-socket(7)* for details
|
||||||
|
|
||||||
*escape <key>*
|
*escape <key>*
|
||||||
Set <key> to switch to root mode to execute one command
|
Set <key> to switch to root mode to execute one command
|
||||||
|
|
||||||
|
|
@ -170,10 +182,13 @@ definekey top <key> mode root
|
||||||
Enable or disable disable-while-typing for specified input
|
Enable or disable disable-while-typing for specified input
|
||||||
device
|
device
|
||||||
|
|
||||||
*event enabled|disabled|disabled_on_external_mouse*
|
*events enabled|disabled|disabled_on_external_mouse*
|
||||||
Enable or disable send_events for specified input device -
|
Enable or disable send_events for specified input device -
|
||||||
Disabling send_events disables the input device.
|
Disabling send_events disables the input device.
|
||||||
|
|
||||||
|
*keybindings [enabled|disabled]*
|
||||||
|
Enable or disable keybinding interpretation for a specific keyboard.
|
||||||
|
|
||||||
*left_handed enabled|disabled*
|
*left_handed enabled|disabled*
|
||||||
Enable or disable left handed mode for specified input device
|
Enable or disable left handed mode for specified input device
|
||||||
|
|
||||||
|
|
@ -200,6 +215,12 @@ definekey top <key> mode root
|
||||||
*scroll_method none|two_finger|edge|on_button_down*
|
*scroll_method none|two_finger|edge|on_button_down*
|
||||||
Change scroll method for specified input device
|
Change scroll method for specified input device
|
||||||
|
|
||||||
|
*repeat_delay <n>*
|
||||||
|
Repeat delay in ms for keyboards only
|
||||||
|
|
||||||
|
*repeat_rate <n>*
|
||||||
|
Repeat rate in 1/s for keyboards only
|
||||||
|
|
||||||
*tap enabled|disabled*
|
*tap enabled|disabled*
|
||||||
Enable or disable tap for specified input device
|
Enable or disable tap for specified input device
|
||||||
|
|
||||||
|
|
@ -218,31 +239,37 @@ message <text>
|
||||||
|
|
||||||
*movetonextscreen*
|
*movetonextscreen*
|
||||||
Move currently focused window to next screen
|
Move currently focused window to next screen
|
||||||
|
See *output* for differences between screen and output.
|
||||||
|
|
||||||
*movetoprevscreen*
|
*movetoprevscreen*
|
||||||
Move currently focused window to previous screen
|
Move currently focused window to previous screen
|
||||||
|
See *output* for differences between screen and output.
|
||||||
|
|
||||||
*movetoscreen <n>*
|
*movetoscreen <n>*
|
||||||
Move currently focused window to <n>-th screen
|
Move currently focused window to <n>-th screen
|
||||||
|
See *output* for differences between screen and output.
|
||||||
|
|
||||||
*movetoworkspace <n>*
|
*movetoworkspace <n>*
|
||||||
Move currently focused window to <n>-th workspace
|
Move currently focused window to <n>-th workspace
|
||||||
|
See *output* for differences between screen and output.
|
||||||
|
|
||||||
*next*
|
*next*
|
||||||
Focus next window in current tile
|
Focus next window in current tile
|
||||||
|
|
||||||
*nextscreen*
|
*nextscreen*
|
||||||
Focus next screen
|
Focus next screen
|
||||||
|
See *output* for differences between screen and output.
|
||||||
|
|
||||||
*only*
|
*only*
|
||||||
Remove all splits and make current window fill the entire screen
|
Remove all splits and make current window fill the entire screen
|
||||||
|
|
||||||
*output <name> [[pos <xpos> <ypos> res <width>x<height> rate <rate>] | enable | disable | prio <n> ]*
|
*output <name> [[pos <xpos> <ypos> res <width>x<height> rate <rate> [scale <scale>]] | enable | disable | prio <n> | rotate <n>]*
|
||||||
Configure output "<name>" -
|
Configure output "<name>" -
|
||||||
- <xpos> and <ypos> are the position of the
|
- <xpos> and <ypos> are the position of the
|
||||||
monitor in pixels. The top-left monitor should have the coordinates 0 0.
|
monitor in pixels. The top-left monitor should have the coordinates 0 0.
|
||||||
- <width> and <height> specify the resolution in pixels.
|
- <width> and <height> specify the resolution in pixels.
|
||||||
- <rate> sets the refresh rate of the monitor (often this is 50 or 60).
|
- <rate> sets the refresh rate of the monitor (often this is 50 or 60).
|
||||||
|
- <scale> sets the output scale (default is 1.0)
|
||||||
- enable and disable enable or disable <name>. Note that if
|
- enable and disable enable or disable <name>. Note that if
|
||||||
<output> is the only enabled output, *output <output> disable* has
|
<output> is the only enabled output, *output <output> disable* has
|
||||||
no effect.
|
no effect.
|
||||||
|
|
@ -252,6 +279,30 @@ message <text>
|
||||||
to set priorities for outputs, where <n> >= 1. The larger <n> is,
|
to set priorities for outputs, where <n> >= 1. The larger <n> is,
|
||||||
the higher the priority is, that is to say, the earlier the output
|
the higher the priority is, that is to say, the earlier the output
|
||||||
will appear in the list of outputs.
|
will appear in the list of outputs.
|
||||||
|
- rotate <n> is used to rotate the output by `<n> mod 4 x 90` degrees
|
||||||
|
counter-clockwise.
|
||||||
|
|
||||||
|
```
|
||||||
|
# Don't rotate
|
||||||
|
output DP-1 rotate 0
|
||||||
|
|
||||||
|
# rotate 90 degrees counter-clockwise
|
||||||
|
output DP-1 rotate 1
|
||||||
|
|
||||||
|
# rotate 180 degrees counter-clockwise
|
||||||
|
output DP-1 rotate 2
|
||||||
|
|
||||||
|
# rotate 270 degrees counter-clockwise
|
||||||
|
output DP-1 rotate 3
|
||||||
|
```
|
||||||
|
|
||||||
|
*output* and the *screen* family of commands are similar in that they
|
||||||
|
both deal with monitors on some level.
|
||||||
|
- *output* addresses outputs by their name and is vaguely symmetric
|
||||||
|
to *input*.
|
||||||
|
- Any *screen* command deals with the number identifying a
|
||||||
|
monitor within a Cagebreak session either explicitly or
|
||||||
|
implicitly (i.e. the commands containing next and prev).
|
||||||
|
|
||||||
*prev*
|
*prev*
|
||||||
Focus previous window in current tile
|
Focus previous window in current tile
|
||||||
|
|
@ -276,6 +327,7 @@ message <text>
|
||||||
|
|
||||||
*screen <n>*
|
*screen <n>*
|
||||||
Change to <n>-th screen
|
Change to <n>-th screen
|
||||||
|
See *output* for differences between screen and output.
|
||||||
|
|
||||||
*show_info*
|
*show_info*
|
||||||
Display information about the current setup - In particular, print the identifiers
|
Display information about the current setup - In particular, print the identifiers
|
||||||
|
|
@ -355,6 +407,7 @@ is used.
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
*cagebreak(1)*
|
*cagebreak(1)*
|
||||||
|
*cagebreak-socket(7)*
|
||||||
|
|
||||||
# BUGS
|
# BUGS
|
||||||
|
|
||||||
|
|
@ -370,7 +423,7 @@ GPG Fingerprints:
|
||||||
|
|
||||||
# LICENSE
|
# LICENSE
|
||||||
|
|
||||||
Copyright (c) 2020-2022 The Cagebreak authors
|
Copyright (c) 2020-2023 The Cagebreak authors
|
||||||
|
|
||||||
Copyright (c) 2018-2020 Jente Hidskes
|
Copyright (c) 2018-2020 Jente Hidskes
|
||||||
|
|
||||||
|
|
|
||||||
606
man/cagebreak-socket.7.md
Normal file
606
man/cagebreak-socket.7.md
Normal file
|
|
@ -0,0 +1,606 @@
|
||||||
|
cagebreak-socket(7) "Version 2.0.0" "Cagebreak Manual"
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
*cagebreak-socket* — Cagebreak socket
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
|
||||||
|
*ipc-socket-capable-tool \$CAGEBREAK_SOCKET*
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
The cagebreak socket is an ipc socket.
|
||||||
|
|
||||||
|
The socket is enabled if cagebreak is invoked with the `-e` flag.
|
||||||
|
|
||||||
|
The socket accepts cagebreak commands as input (see *cagebreak-config(5)* for more information).
|
||||||
|
|
||||||
|
Events are provided as output as specified in this man page.
|
||||||
|
|
||||||
|
## EVENTS
|
||||||
|
|
||||||
|
Events have a general structure as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
"cg-ipc"json object depending on the eventNULL
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is an example of how this works using *only* as a command sent over the socket.
|
||||||
|
|
||||||
|
```
|
||||||
|
only
|
||||||
|
cg-ipc{"event_name":"fullscreen","tile_id":2,"workspace":1,"output":"eDP-1"}
|
||||||
|
```
|
||||||
|
|
||||||
|
This documentation describes the trigger for the events, the keys and the data
|
||||||
|
type of the values of each event.
|
||||||
|
|
||||||
|
*background*
|
||||||
|
- Trigger: *background* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "background"
|
||||||
|
- old_bg: list of three floating point numbers denoting the old background in rgb
|
||||||
|
- new_bg: list of three floating point numbers denoting the new background in rgb
|
||||||
|
|
||||||
|
```
|
||||||
|
background 0 1.0 0
|
||||||
|
cg-ipc{"event_name":"background","old_bg":[0.000000,1.000000,1.000000],"new_bg":[0.000000,1.000000,0.000000]}
|
||||||
|
```
|
||||||
|
|
||||||
|
*close*
|
||||||
|
- Trigger: *close* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "close"
|
||||||
|
- view_id: view id as an integer
|
||||||
|
- tile_id: tile id as an integer
|
||||||
|
- view_pid: pid of the process
|
||||||
|
- workspace: workspace number as an integer
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
close
|
||||||
|
cg-ipc{"event_name":"close","view_id":47,"view_pid":30456,"tile_id":47,"workspace":1,"output":"eDP-1","output_id":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
*configure_input*
|
||||||
|
- Trigger: *input* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "configure_input"
|
||||||
|
- input: the input as a string, as per cagebreak-config(5)
|
||||||
|
|
||||||
|
```
|
||||||
|
input * accel_profile flat
|
||||||
|
cg-ipc{"event_name":"configure_input","input":"*"}
|
||||||
|
```
|
||||||
|
|
||||||
|
*configure_message*
|
||||||
|
- Trigger: *configure_message* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "configure_message"
|
||||||
|
|
||||||
|
```
|
||||||
|
configure_message fg_color 1.0 1.0 0 0
|
||||||
|
cg-ipc{"event_name":"configure_message"}
|
||||||
|
```
|
||||||
|
|
||||||
|
*configure_output*
|
||||||
|
- Trigger: *output* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "configure_output"
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
output eDP-1 rotate 0
|
||||||
|
cg-ipc{"event_name":"configure_output","output":"eDP-1","output_id":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
*cursor_switch_tile*
|
||||||
|
- Trigger: Cursor crosses the border between tiles
|
||||||
|
- JSON
|
||||||
|
- event_name: "cursor_switch_tile"
|
||||||
|
- old_output: name of the old output as a string
|
||||||
|
- old_output_id: old output id as an integer
|
||||||
|
- old_tile: number of the old tile as an integer
|
||||||
|
- new_output: name of the new output as a string
|
||||||
|
- new_output_id: new output id as an integer
|
||||||
|
- new_tile: number of the new tile as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
# Cursor switches tile
|
||||||
|
cg-ipc{"event_name":"cursor_switch_tile","old_output":"eDP-1","old_output_id":1,"old_tile":2,"new_output":"eDP-1","new_output_id":1,"new_tile":3}
|
||||||
|
```
|
||||||
|
|
||||||
|
*cycle_outputs*
|
||||||
|
- Trigger: *nextscreen* and *prevscreen* commands
|
||||||
|
- JSON
|
||||||
|
- event_name: "cycle_outputs"
|
||||||
|
- old_output: old output name as string
|
||||||
|
- old_output_id: old output id as an integer
|
||||||
|
- new_output: new output name as string
|
||||||
|
- new_output_id: new output id as an integer
|
||||||
|
- reverse: "0" if *nextscreen* or "1" if *prevscreen*
|
||||||
|
|
||||||
|
```
|
||||||
|
nextscreen
|
||||||
|
cg-ipc{"event_name":"cycle_outputs","old_output":"eDP-1","old_output_id":1,"new_output":"HDMI-A-1","new_output_id":2,"reverse":0}
|
||||||
|
```
|
||||||
|
|
||||||
|
*cycle_views*
|
||||||
|
- Trigger: *next* and *prev* commands
|
||||||
|
- JSON
|
||||||
|
- event_name: "cycle_views"
|
||||||
|
- old_view_id: old view id as an integer
|
||||||
|
- old_view_pid: pid of old view
|
||||||
|
- new_view_id: new view id as an interger
|
||||||
|
- new_view_pid: pid of new view
|
||||||
|
- tile_id: tile id as an integer
|
||||||
|
- workspace: workspace number as an integer
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
next
|
||||||
|
cg-ipc{"event_name":"cycle_views","old_view_id":11,"old_view_pid":32223,"new_view_id":4,"old_view_pid";53221,"tile_id":13,"workspace":1,"output":"eDP-1","output_id":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
*definekey*
|
||||||
|
- Trigger: *definekey* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "definekey"
|
||||||
|
- modifiers: number denoting the modifier as described below
|
||||||
|
- 0: no modifier
|
||||||
|
- 1: shift
|
||||||
|
- 2: alt
|
||||||
|
- 3: ctrl
|
||||||
|
- 4: logo key
|
||||||
|
- 5: modifier 2
|
||||||
|
- 6: modifier 3
|
||||||
|
- 7: modifier 5
|
||||||
|
- key: key as a number
|
||||||
|
- command: command as a string - CAVEAT: This is an internal representation of
|
||||||
|
commands which is not in one-to-one correspondance with the commands available in the config file. The differences are as follows:
|
||||||
|
- "cycle_tiles": represents any *exchange* command (e.g. *exchangeright*)
|
||||||
|
- "cycle_views": represents both the *next* and the *prev* command
|
||||||
|
- "cycle_outputs": represents *nextscreen*, *movetoprevscreen* and *prevscreen* commands
|
||||||
|
- "resize_tile_vertical": represents *resizedown* and *resizeup* commands
|
||||||
|
- "resize_tile_horizontal": represents *resizeleft* and *resizeright* commands
|
||||||
|
|
||||||
|
```
|
||||||
|
definemode foo
|
||||||
|
cg-ipc{"event_name":"definemode","mode":"foo"}
|
||||||
|
definekey foo e only
|
||||||
|
cg-ipc{"event_name":"definekey","modifiers":0,"key":101,"command":"only"}
|
||||||
|
```
|
||||||
|
|
||||||
|
*definemode*
|
||||||
|
- Trigger: *definemode* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "definemode"
|
||||||
|
- mode: name of mode as string
|
||||||
|
|
||||||
|
```
|
||||||
|
definemode foo
|
||||||
|
cg-ipc{"event_name":"definemode","mode":"foo"}
|
||||||
|
```
|
||||||
|
|
||||||
|
*destroy_output*
|
||||||
|
- Trigger: removal of an output
|
||||||
|
- JSON
|
||||||
|
- event_name: "destroy_output"
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
# remove output from the device
|
||||||
|
cg-ipc{"event_name":"destroy_output","output":"HDMI-A-1","output_id":2}
|
||||||
|
```
|
||||||
|
|
||||||
|
*dump*
|
||||||
|
- Trigger: *dump* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "dump"
|
||||||
|
- nws: number of workspaces as an integer
|
||||||
|
- bg_color: list of three floating point numbers denoting the new background in rgb
|
||||||
|
- views_curr_id: id of the currently focussed view as an integer
|
||||||
|
- tiles_curr_id: id of the currently focussed tile as in integer
|
||||||
|
- curr_output: current output as a string
|
||||||
|
- default_mode: name of the default mode as a string
|
||||||
|
- modes: list of names of modes as strings
|
||||||
|
- outputs: object of objects for each output
|
||||||
|
- output name as string
|
||||||
|
- priority: priority as per *output* prio <n> in *cagebreak-config(5)* or default
|
||||||
|
- coords: object of x and y coordinates of output
|
||||||
|
- size: object of width and height as integers
|
||||||
|
- refresh_rate: refresh rate as float
|
||||||
|
- curr_workspace: current workspace as an integer
|
||||||
|
- workspaces: list of objects for each workspace
|
||||||
|
- views: list of objects for each view
|
||||||
|
- id: view id as an integer
|
||||||
|
- pid: pid of the process which opened the view as an integer
|
||||||
|
- coords: object of x and y coordinates
|
||||||
|
- type: ["xdg"|"xwayland"]
|
||||||
|
- tiles: list of objects for all tiles
|
||||||
|
- id: tile id as an integer
|
||||||
|
- coords: object of x and y coordinates
|
||||||
|
- size: object of width and height
|
||||||
|
- view: view id as an integer
|
||||||
|
- keyboards: object of objects for each keyboard group
|
||||||
|
- keyboard name as a string
|
||||||
|
- commands_enabled: 0 if keybindings are disabled for the keyboard, 1 otherwise
|
||||||
|
- repeat_delay: repeat delay in milliseconds as an integer
|
||||||
|
- repeat_rate: repeat rate in 1/sec as an integer
|
||||||
|
- input_devices: object of objects for each keyboard
|
||||||
|
- identifier for a keyboard as a string
|
||||||
|
- is_virtual: 1 if virtual, 0 otherwise
|
||||||
|
- type: [keyboard|pointer|switch]
|
||||||
|
- cursor_coords: object of x and y coordinates
|
||||||
|
|
||||||
|
```
|
||||||
|
dump
|
||||||
|
cg-ipc{"event_name":"dump","nws":1,
|
||||||
|
"bg_color":[0.000000,0.000000,0.000000],
|
||||||
|
"views_curr_id":80,
|
||||||
|
"tiles_curr_id":8,
|
||||||
|
"curr_output":"eDP-1",
|
||||||
|
"default_mode":"top",
|
||||||
|
"modes":["top","root","resize"],
|
||||||
|
"outputs": {"eDP-1": {
|
||||||
|
"priority": -1,
|
||||||
|
"coords": {"x":0,"y":0},
|
||||||
|
"size": {"width":2560,"height":1440},
|
||||||
|
"refresh_rate": 60.012000,
|
||||||
|
"curr_workspace": 0,
|
||||||
|
"workspaces": [{"views": [{
|
||||||
|
"id": 16,
|
||||||
|
"pid": 2505,
|
||||||
|
"coords": {"x":0,"y":0},
|
||||||
|
"type": "xwayland"
|
||||||
|
|
||||||
|
},{
|
||||||
|
"id": 72,
|
||||||
|
"pid": 11243,
|
||||||
|
"coords": {"x":0,"y":0},
|
||||||
|
"type": "xdg"
|
||||||
|
|
||||||
|
},{
|
||||||
|
"id": 56,
|
||||||
|
"pid": 6700,
|
||||||
|
"coords": {"x":1280,"y":0},
|
||||||
|
"type": "xdg"
|
||||||
|
|
||||||
|
}],"tiles": [{
|
||||||
|
"id": 6,
|
||||||
|
"coords": {"x":0,"y":0},
|
||||||
|
"size": {"width":1280,"height":1440},
|
||||||
|
"view": 78
|
||||||
|
|
||||||
|
},{
|
||||||
|
"id": 7,
|
||||||
|
"coords": {"x":1280,"y":0},
|
||||||
|
"size": {"width":1280,"height":1440},
|
||||||
|
"view": 42
|
||||||
|
|
||||||
|
}]}]
|
||||||
|
}}
|
||||||
|
,"keyboards": {"0:1:Power_Button": {
|
||||||
|
"commands_enabled": 1,
|
||||||
|
"repeat_delay": 600,
|
||||||
|
"repeat_rate": 25
|
||||||
|
}}
|
||||||
|
,"input_devices": {"6058:20564:ThinkPad_Extra_Buttons": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "switch",
|
||||||
|
},"6058:20564:ThinkPad_Extra_Buttons": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "keyboard",
|
||||||
|
},"2:10:TPPS/2_Elan_TrackPoint": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "pointer",
|
||||||
|
},"1:1:AT_Translated_Set_2_keyboard": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "keyboard",
|
||||||
|
},"0:0:sof-hda-dsp_Headphone": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "keyboard",
|
||||||
|
},"1739:52619:SYNA8004:00_06CB:CD8B_Touchpad": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "pointer",
|
||||||
|
},"1739:52619:SYNA8004:00_06CB:CD8B_Mouse": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "pointer",
|
||||||
|
},"0:3:Sleep_Button": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "keyboard",
|
||||||
|
},"0:5:Lid_Switch": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "switch",
|
||||||
|
},"0:6:Video_Bus": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "keyboard",
|
||||||
|
},"0:1:Power_Button": {
|
||||||
|
"is_virtual": 0,
|
||||||
|
"type": "keyboard",
|
||||||
|
}}
|
||||||
|
,"cursor_coords":{"x":972.821761,"y":670.836215}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*focus_tile*
|
||||||
|
- Trigger: *focus* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "focus_tile"
|
||||||
|
- old_tile_id: old tile id as an integer
|
||||||
|
- new_tile_id: new tile id as an integer
|
||||||
|
- workspace: workspace number as an integer
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
focus
|
||||||
|
cg-ipc{"event_name":"focus_tile","old_tile_id":14,"new_tile_id":13,"workspace":1,"output":"eDP-1","output_id":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
*fullscreen*
|
||||||
|
- Trigger: *only* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "fullscreen"
|
||||||
|
- tile_id: tile id as an integer
|
||||||
|
- workspace: workspace number as an integer
|
||||||
|
- output: output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
only
|
||||||
|
cg-ipc{"event_name":"fullscreen","tile_id":3,"workspace":1,"output":"eDP-1","output_id":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
*move_view_to_cycle_output*
|
||||||
|
- Trigger: *movetonextscreen* and similar commands
|
||||||
|
- JSON
|
||||||
|
- event_name: "move_view_to_cycle_output"
|
||||||
|
- view_id: view id as an integer
|
||||||
|
- view_pid: pid of the process
|
||||||
|
- old_output: name of the old output as a string
|
||||||
|
- old_output_id: old output id as an integer
|
||||||
|
- new_output: name of the new output as a string
|
||||||
|
- old_output_id: old output id as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
movetonextscreen
|
||||||
|
cg-ipc{"event_name":"cycle_outputs","old_output":"eDP-1","new_output":"HDMI-A-1","reverse":0}
|
||||||
|
cg-ipc{"event_name":"move_view_to_cycle_output","view_id":11,"view_pid":43123,"old_output":"eDP-1","old_output_id":1,"new_output":"HDMI-A-1","new_output_id":2}
|
||||||
|
```
|
||||||
|
|
||||||
|
*move_view_to_output*
|
||||||
|
- Trigger: *movetoscreen* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "move_view_to_output"
|
||||||
|
- view_id: view id as an integer
|
||||||
|
- old_output: old output name as string
|
||||||
|
- new_output: new output name as string
|
||||||
|
|
||||||
|
```
|
||||||
|
movetoscreen 2
|
||||||
|
cg-ipc{"event_name":"switch_output","old_output":"eDP-1","new_output":"HDMI-A-1"}
|
||||||
|
cg-ipc{"event_name":"move_view_to_output","view_id":78,"old_output":"eDP-1","new_output":"HDMI-A-1"}
|
||||||
|
```
|
||||||
|
|
||||||
|
*move_view_to_ws*
|
||||||
|
- Trigger: *movetoworkspace* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "move_view_to_ws"
|
||||||
|
- view_id: view id as an integer
|
||||||
|
- old_workspace: old workspace number as an integer
|
||||||
|
- new_workspace: new workspace number as an integer
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
- view_pid: pid of the process
|
||||||
|
|
||||||
|
```
|
||||||
|
movetoworkspace 1
|
||||||
|
cg-ipc{"event_name":"switch_ws","old_workspace":1,"new_workspace":1,"output":"eDP-1"}
|
||||||
|
cg-ipc{"event_name":"move_view_to_ws","view_id":43,"old_workspace":0,"new_workspace":0,"output":"eDP-1","output_id":1,"view_pid":64908}
|
||||||
|
```
|
||||||
|
|
||||||
|
*new_output*
|
||||||
|
- Trigger: a new output is physically attached
|
||||||
|
- JSON
|
||||||
|
- event_name: "new_output"
|
||||||
|
- output: new output name as a string
|
||||||
|
- output_id: id of the new output as an integer
|
||||||
|
- priority: priority as per *output* prio <n> in *cagebreak-config(5)* or default
|
||||||
|
|
||||||
|
```
|
||||||
|
# a new output is attached
|
||||||
|
cg-ipc{"event_name":"new_output","output":"HDMI-A-1","output_id":2,"priority":-1}
|
||||||
|
```
|
||||||
|
|
||||||
|
*resize_tile*
|
||||||
|
- Trigger: the *resize* family of commands
|
||||||
|
- JSON
|
||||||
|
- event_name: "resize_tile"
|
||||||
|
- tile_id: tile id as an integer
|
||||||
|
- old_dims: list of coordinates [x coordinate of lower left corner, y coordinate of lower left corner, x coordinate of upper right corner, y coordinate of upper right corner]
|
||||||
|
- new_dims: list of coordinate [x coordinate of lower left corner, y coordinate of lower left corner, x coordinate of upper right corner, y coordinate of upper right corner]
|
||||||
|
- workspace: workspace number as an integer
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
resizeleft
|
||||||
|
cg-ipc{"event_name":"resize_tile","tile_id":14,"old_dims":"[1280,0,1440,1280]","new_dims":"[1270,0,1440,1290]","workspace":1,"output":"eDP-1","output_id":1}
|
||||||
|
cg-ipc{"event_name":"resize_tile","tile_id":13,"old_dims":"[0,0,1440,1280]","new_dims":"[0,0,1440,1270]","workspace":1,"output":"eDP-1","output_id":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
*set_nws*
|
||||||
|
- Trigger: *workspaces* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "set_nws"
|
||||||
|
- old_nws: old number of workspaces as an integer
|
||||||
|
- new_nws: new number of workspaces as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
workspaces 2
|
||||||
|
cg-ipc{"event_name":"set_nws","old_nws":1,"new_nws":2}
|
||||||
|
```
|
||||||
|
|
||||||
|
*split*
|
||||||
|
- Trigger: *split* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "split"
|
||||||
|
- tile_id: old tile id as an integer
|
||||||
|
- new_tile_id: new tile id as an integer
|
||||||
|
- workspace: workspace number as an integer
|
||||||
|
- output: output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
- vertical: 0 if horizontal split, 1 if not
|
||||||
|
|
||||||
|
```
|
||||||
|
hsplit
|
||||||
|
cg-ipc{"event_name":"split","tile_id":11,"new_tile_id":12,"workspace":1,"output":"eDP-1","output_id":1,"vertical":0}
|
||||||
|
```
|
||||||
|
|
||||||
|
*swap_tile*
|
||||||
|
- Trigger: the *exchange* family of commands
|
||||||
|
- JSON
|
||||||
|
- event_name: "swap_tile"
|
||||||
|
- tile_id: previous tile id as an integer
|
||||||
|
- tile_pid: pid of previous tile
|
||||||
|
- swap_tile_id: swap tile id as an integer
|
||||||
|
- swap_tile_pid: pid of swap tile
|
||||||
|
- workspace: workspace number as an integer
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
exchangeright
|
||||||
|
cg-ipc{"event_name":"swap_tile","tile_id":1,"tile_pid":53478,"swap_tile_id":3,"swap_tile_pid":98234,"workspace":1,"output":"eDP-1"}
|
||||||
|
```
|
||||||
|
|
||||||
|
*switch_default_mode*
|
||||||
|
- Trigger: *setmode* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "switch_default_mode"
|
||||||
|
- old_mode: old mode name
|
||||||
|
- mode: new mode name
|
||||||
|
|
||||||
|
```
|
||||||
|
setmode top
|
||||||
|
cg-ipc{"event_name":"switch_default_mode","old_mode":"top","mode":"root"}
|
||||||
|
```
|
||||||
|
|
||||||
|
switch_output
|
||||||
|
- Trigger. *screen* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "switch_output"
|
||||||
|
- old_output: name of the old output as a string
|
||||||
|
- old_output_id: old output id as an integer
|
||||||
|
- new_output: name of the new output as a string
|
||||||
|
- new_output_id: new output id as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
screen 2
|
||||||
|
cg-ipc{"event_name":"switch_output","old_output":"eDP-1","old_output_id":1,"new_output":"HDMI-A-1","new_output_id":2}
|
||||||
|
```
|
||||||
|
|
||||||
|
switch_ws
|
||||||
|
- Trigger: *workspace* command
|
||||||
|
- JSON
|
||||||
|
- event_name: "switch_ws"
|
||||||
|
- old_workspace: old workspace number as an integer
|
||||||
|
- new_workspace: new workspace number as an integer
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
|
||||||
|
```
|
||||||
|
workspace 2
|
||||||
|
cg-ipc{"event_name":"switch_ws","old_workspace":1,"new_workspace":2,"output":"eDP-1","output_id":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
*view_map*
|
||||||
|
- Trigger: view is opened by a process
|
||||||
|
- JSON
|
||||||
|
- event_name: "view_map"
|
||||||
|
- view_id: view id as an integer
|
||||||
|
- tile_id: tile id as an integer
|
||||||
|
- workspace: workspace number as an integer
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
- view_pid: pid of the process
|
||||||
|
|
||||||
|
```
|
||||||
|
# process opens a view
|
||||||
|
cg-ipc{"event_name":"view_map","view_id":28,"tile_id":14,"workspace":1,"output":"eDP-1","output_id":1,"view_pid":39827}
|
||||||
|
```
|
||||||
|
|
||||||
|
*view_unmap*
|
||||||
|
- Trigger: view is closed by a process
|
||||||
|
- JSON
|
||||||
|
- event_name: "view_unmap"
|
||||||
|
- view_id: view id as an integer
|
||||||
|
- tile_id: tile id as an integer
|
||||||
|
- workspace: workspace number as an integer
|
||||||
|
- output: name of the output as a string
|
||||||
|
- output_id: id of the output as an integer
|
||||||
|
- view_pid: pid of the process
|
||||||
|
|
||||||
|
```
|
||||||
|
# view is closed by the process
|
||||||
|
cg-ipc{"event_name":"view_unmap","view_id":24,"tile_id":13,"workspace":1,"output":"eDP-1","output_id":1,"view_pid":39544}
|
||||||
|
```
|
||||||
|
|
||||||
|
## SECURITY
|
||||||
|
|
||||||
|
The socket has to be explicitly enabled using the `-e` flag.
|
||||||
|
|
||||||
|
The socket is restricted to the user for reading, writing and execution (700).
|
||||||
|
|
||||||
|
All user software can execute arbitrary code while the cagebreak socket is running.
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
*nc -U $CAGEBREAK_SOCKET*
|
||||||
|
|
||||||
|
*ncat -U $CAGEBREAK_SOCKET*
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*cagebreak(1)*
|
||||||
|
*cagebreak-config(5)*
|
||||||
|
|
||||||
|
# BUGS
|
||||||
|
|
||||||
|
See GitHub Issues: <https://github.com/project-repo/cagebreak/issues>
|
||||||
|
|
||||||
|
Mail contact: `cagebreak @ project-repo . co`
|
||||||
|
|
||||||
|
GPG Fingerprints:
|
||||||
|
|
||||||
|
- B15B92642760E11FE002DE168708D42451A94AB5
|
||||||
|
- F8DD9F8DD12B85A28F5827C4678E34D2E753AA3C
|
||||||
|
- 3ACEA46CCECD59E4C8222F791CBEB493681E8693
|
||||||
|
|
||||||
|
# LICENSE
|
||||||
|
|
||||||
|
Copyright (c) 2022 - 2023 The Cagebreak authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cagebreak(1) "Version 1.9.1" "Cagebreak Manual"
|
cagebreak(1) "Version 2.0.0" "Cagebreak Manual"
|
||||||
|
|
||||||
# NAME
|
# NAME
|
||||||
|
|
||||||
|
|
@ -32,12 +32,15 @@ are displayed in a message box at the top right of the screen.
|
||||||
|
|
||||||
# OPTIONS
|
# OPTIONS
|
||||||
|
|
||||||
|
*-c <path>*
|
||||||
|
Load configuration file from <path>
|
||||||
|
|
||||||
|
*-e*
|
||||||
|
Enable socket
|
||||||
|
|
||||||
*-h*
|
*-h*
|
||||||
Display help message and exit
|
Display help message and exit
|
||||||
|
|
||||||
*-r*
|
|
||||||
Rotate the output 90 degrees clockwise, can be specified up to three times
|
|
||||||
|
|
||||||
*-s*
|
*-s*
|
||||||
Show all available inputs and outputs
|
Show all available inputs and outputs
|
||||||
|
|
||||||
|
|
@ -68,6 +71,7 @@ are displayed in a message box at the top right of the screen.
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
*cagebreak-config(5)*
|
*cagebreak-config(5)*
|
||||||
|
*cagebreak-socket(7)*
|
||||||
|
|
||||||
# BUGS
|
# BUGS
|
||||||
|
|
||||||
|
|
@ -83,7 +87,7 @@ GPG Fingerprints:
|
||||||
|
|
||||||
# LICENSE
|
# LICENSE
|
||||||
|
|
||||||
Copyright (c) 2020-2022 The Cagebreak authors
|
Copyright (c) 2020-2023 The Cagebreak authors
|
||||||
|
|
||||||
Copyright (c) 2018-2020 Jente Hidskes
|
Copyright (c) 2018-2020 Jente Hidskes
|
||||||
|
|
||||||
|
|
|
||||||
68
meson.build
68
meson.build
|
|
@ -1,10 +1,11 @@
|
||||||
project('cagebreak', 'c',
|
# Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
version: '1.9.1',
|
# SPDX -License-Identifier: MIT
|
||||||
license: 'MIT',
|
project(
|
||||||
default_options: [
|
'cagebreak',
|
||||||
'c_std=c11',
|
'c',
|
||||||
'warning_level=3',
|
version : '2.0.0',
|
||||||
],
|
license : 'MIT',
|
||||||
|
default_options : ['c_std=c11', 'warning_level=3']
|
||||||
)
|
)
|
||||||
|
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
|
|
@ -53,12 +54,11 @@ if is_freebsd
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
wlroots = dependency('wlroots', version: ['>=0.14.1', '< 0.16.0'])
|
wlroots = dependency('wlroots', version: ['>=0.16.1', '< 0.17.0'])
|
||||||
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
|
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
|
||||||
wayland_server = dependency('wayland-server')
|
wayland_server = dependency('wayland-server')
|
||||||
wayland_cursor = dependency('wayland-cursor')
|
wayland_cursor = dependency('wayland-cursor')
|
||||||
wayland_client = dependency('wayland-client')
|
wayland_client = dependency('wayland-client')
|
||||||
pixman = dependency('pixman-1')
|
|
||||||
xkbcommon = dependency('xkbcommon')
|
xkbcommon = dependency('xkbcommon')
|
||||||
cairo = dependency('cairo')
|
cairo = dependency('cairo')
|
||||||
pango = dependency('pango')
|
pango = dependency('pango')
|
||||||
|
|
@ -132,7 +132,6 @@ cagebreak_source_strings = [
|
||||||
'workspace.c',
|
'workspace.c',
|
||||||
'output.c',
|
'output.c',
|
||||||
'parse.c',
|
'parse.c',
|
||||||
'render.c',
|
|
||||||
'seat.c',
|
'seat.c',
|
||||||
'util.c',
|
'util.c',
|
||||||
'view.c',
|
'view.c',
|
||||||
|
|
@ -150,7 +149,6 @@ cagebreak_header_strings = [
|
||||||
'workspace.h',
|
'workspace.h',
|
||||||
'output.h',
|
'output.h',
|
||||||
'parse.h',
|
'parse.h',
|
||||||
'render.h',
|
|
||||||
'seat.h',
|
'seat.h',
|
||||||
'server.h',
|
'server.h',
|
||||||
'util.h',
|
'util.h',
|
||||||
|
|
@ -194,7 +192,6 @@ cagebreak_dependencies_dict = {
|
||||||
'libinput': [libinput,true],
|
'libinput': [libinput,true],
|
||||||
'libevdev': [libevdev,true],
|
'libevdev': [libevdev,true],
|
||||||
'libudev': [libudev,true],
|
'libudev': [libudev,true],
|
||||||
'pixman': [pixman,true],
|
|
||||||
'pango': [pango,true],
|
'pango': [pango,true],
|
||||||
'cairo': [cairo,true],
|
'cairo': [cairo,true],
|
||||||
'pangocairo': [pangocairo,true],
|
'pangocairo': [pangocairo,true],
|
||||||
|
|
@ -206,16 +203,15 @@ reproducible_build_versions = {
|
||||||
'wayland_server': '1.19.0',
|
'wayland_server': '1.19.0',
|
||||||
'wayland_client': '1.21.0',
|
'wayland_client': '1.21.0',
|
||||||
'wayland_cursor': '1.21.0',
|
'wayland_cursor': '1.21.0',
|
||||||
'wlroots': '0.15.1',
|
'wlroots': '0.16.1',
|
||||||
'xkbcommon': '1.4.1',
|
'xkbcommon': '1.5.0',
|
||||||
'fontconfig': '2.14.0',
|
'fontconfig': '2.14.1',
|
||||||
'libinput': '1.21.0',
|
'libinput': '1.22.0',
|
||||||
'libevdev': '1.12.1',
|
'libevdev': '1.13.0',
|
||||||
'libudev': '251',
|
'libudev': '252',
|
||||||
'pixman': '0.40.0',
|
'pango': '1.50.12',
|
||||||
'pango': '1.50.8',
|
|
||||||
'cairo': '1.17.6',
|
'cairo': '1.17.6',
|
||||||
'pangocairo': '1.50.8',
|
'pangocairo': '1.50.12',
|
||||||
'math': '-1'
|
'math': '-1'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,11 +225,11 @@ foreach name, dep : cagebreak_dependencies_dict
|
||||||
if reproducible_build_versions[name] != '-1'
|
if reproducible_build_versions[name] != '-1'
|
||||||
if dep[1] == false
|
if dep[1] == false
|
||||||
if dep[0].version() < reproducible_build_versions[name]
|
if dep[0].version() < reproducible_build_versions[name]
|
||||||
warning('The installed version of "' + name + '" on your machine (' + dep[0].version() + ') is older than the one used to generate the binary specified in the README section "Reproducible Builds" (' + reproducible_build_versions[name] + '). It is recommended to use an up-to-date version for compiling cagebreak.'
|
warning('The installed version of "' + name + '" on your machine (' + dep[0].version() + ') is older than the one used to generate the binary specified in Hashes.md (' + reproducible_build_versions[name] + '). It is recommended to use an up-to-date version for compiling cagebreak.'
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
elif reproducible_build_versions[name] != dep[0].version()
|
elif reproducible_build_versions[name] != dep[0].version()
|
||||||
warning('The installed version of "' + name + '" on your machine (' + dep[0].version() + ') differs from the one used to generate the binary specified in the README section "Reproducible Builds" (' + reproducible_build_versions[name] + '). Cagebreak does not guarantee a reproducible build for this configuration.'
|
warning('The installed version of "' + name + '" on your machine (' + dep[0].version() + ') differs from the one used to generate the binary specified in Hashes.md (' + reproducible_build_versions[name] + '). Cagebreak does not guarantee a reproducible build for this configuration.'
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
endif
|
endif
|
||||||
|
|
@ -241,12 +237,12 @@ foreach name, dep : cagebreak_dependencies_dict
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
reproducible_build_compiler = 'gcc'
|
reproducible_build_compiler = 'gcc'
|
||||||
reproducible_build_compiler_version = '12.1.0'
|
reproducible_build_compiler_version = '12.2.0'
|
||||||
|
|
||||||
if cc.get_id() != reproducible_build_compiler
|
if cc.get_id() != reproducible_build_compiler
|
||||||
warning('The compiler "' + cc.get_id() + '" differs from the one used to generate to binary specified in the README section "Reproducible Builds" (' + reproducible_build_compiler + ').')
|
warning('The compiler "' + cc.get_id() + '" differs from the one used to generate to binary specified in Hashes.md (' + reproducible_build_compiler + ').')
|
||||||
elif cc.version() != reproducible_build_compiler_version
|
elif cc.version() != reproducible_build_compiler_version
|
||||||
warning('The version of ' + cc.get_id() + ' (' + cc.version() + ') differs from the one used to generate the binary specified in the README section "Reproducible Builds" ' + reproducible_build_compiler_version + '.')
|
warning('The version of ' + cc.get_id() + ' (' + cc.version() + ') differs from the one used to generate the binary specified in Hashes.md ' + reproducible_build_compiler_version + '.')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
|
|
@ -262,15 +258,18 @@ install_data('examples/config', install_dir : '/etc/xdg/cagebreak')
|
||||||
|
|
||||||
if get_option('man-pages')
|
if get_option('man-pages')
|
||||||
scdoc = find_program('scdoc')
|
scdoc = find_program('scdoc')
|
||||||
|
secssinceepoch = 1673193959
|
||||||
|
shcommand = 'export SOURCE_DATE_EPOCH=' + secssinceepoch.to_string() + ' ; @0@ < @INPUT@'.format(scdoc.path())
|
||||||
sh = find_program('sh')
|
sh = find_program('sh')
|
||||||
mandir1 = join_paths(get_option('mandir'), 'man1')
|
mandir1 = join_paths(get_option('mandir'), 'man1')
|
||||||
mandir5 = join_paths(get_option('mandir'), 'man5')
|
mandir5 = join_paths(get_option('mandir'), 'man5')
|
||||||
|
mandir7 = join_paths(get_option('mandir'), 'man7')
|
||||||
|
|
||||||
cagebreak_man = custom_target('cagebreak_man',
|
cagebreak_man = custom_target('cagebreak_man',
|
||||||
output : 'cagebreak.1',
|
output : 'cagebreak.1',
|
||||||
input : 'man/cagebreak.1.md',
|
input : 'man/cagebreak.1.md',
|
||||||
capture : true,
|
capture : true,
|
||||||
command : [sh, '-c', 'export SOURCE_DATE_EPOCH=1658688233 ; @0@ < @INPUT@'.format(scdoc.path())],
|
command : [sh, '-c', shcommand],
|
||||||
install: true,
|
install: true,
|
||||||
install_dir: mandir1
|
install_dir: mandir1
|
||||||
)
|
)
|
||||||
|
|
@ -278,11 +277,20 @@ if get_option('man-pages')
|
||||||
cagebreak_man = custom_target('cagebreak_config_man',
|
cagebreak_man = custom_target('cagebreak_config_man',
|
||||||
output : 'cagebreak-config.5',
|
output : 'cagebreak-config.5',
|
||||||
input : 'man/cagebreak-config.5.md',
|
input : 'man/cagebreak-config.5.md',
|
||||||
capture : true,
|
capture : true,
|
||||||
command : [sh, '-c', 'export SOURCE_DATE_EPOCH=1658688233 ; @0@ < @INPUT@'.format(scdoc.path())],
|
command : [sh, '-c', shcommand],
|
||||||
install: true,
|
install: true,
|
||||||
install_dir: mandir5
|
install_dir: mandir5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cagebreak_man = custom_target('cagebreak_socket_man',
|
||||||
|
output : 'cagebreak-socket.7',
|
||||||
|
input : 'man/cagebreak-socket.7.md',
|
||||||
|
capture : true,
|
||||||
|
command : [sh, '-c', shcommand],
|
||||||
|
install: true,
|
||||||
|
install_dir: mandir7
|
||||||
|
)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get_option('fuzz')
|
if get_option('fuzz')
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
# Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
# SPDX -License-Identifier: MIT
|
||||||
option('xwayland', type: 'boolean', value: 'false', description: 'Enable support for X11 applications')
|
option('xwayland', type: 'boolean', value: 'false', description: 'Enable support for X11 applications')
|
||||||
option('man-pages', type: 'boolean', value: 'false', description: 'Build man pages (requires pandoc)')
|
option('man-pages', type: 'boolean', value: 'false', description: 'Build man pages (requires pandoc)')
|
||||||
option('fuzz', type: 'boolean', value: 'false', description: 'Enable building fuzzer targets')
|
option('fuzz', type: 'boolean', value: 'false', description: 'Enable building fuzzer targets')
|
||||||
|
|
|
||||||
132
message.c
132
message.c
|
|
@ -1,11 +1,20 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <drm_fourcc.h>
|
#include <drm_fourcc.h>
|
||||||
#include <pango/pangocairo.h>
|
#include <pango/pangocairo.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
|
#include <wlr/interfaces/wlr_buffer.h>
|
||||||
|
#include <wlr/render/allocator.h>
|
||||||
|
#include <wlr/render/drm_format_set.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_output_damage.h>
|
#include <wlr/types/wlr_output_damage.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
|
#include <wlr/types/wlr_scene.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
|
|
@ -14,6 +23,67 @@
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
struct msg_buffer {
|
||||||
|
struct wlr_buffer base;
|
||||||
|
void *data;
|
||||||
|
uint32_t format;
|
||||||
|
size_t stride;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
msg_buffer_destroy(struct wlr_buffer *wlr_buffer) {
|
||||||
|
struct msg_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);
|
||||||
|
free(buffer->data);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
msg_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags,
|
||||||
|
void **data, uint32_t *format,
|
||||||
|
size_t *stride) {
|
||||||
|
struct msg_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);
|
||||||
|
if(data != NULL) {
|
||||||
|
*data = (void *)buffer->data;
|
||||||
|
}
|
||||||
|
if(format != NULL) {
|
||||||
|
*format = buffer->format;
|
||||||
|
}
|
||||||
|
if(stride != NULL) {
|
||||||
|
*stride = buffer->stride;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
msg_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {
|
||||||
|
// This space is intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wlr_buffer_impl msg_buffer_impl = {
|
||||||
|
.destroy = msg_buffer_destroy,
|
||||||
|
.begin_data_ptr_access = msg_buffer_begin_data_ptr_access,
|
||||||
|
.end_data_ptr_access = msg_buffer_end_data_ptr_access,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct msg_buffer *
|
||||||
|
msg_buffer_create(uint32_t width, uint32_t height, uint32_t stride) {
|
||||||
|
struct msg_buffer *buffer = calloc(1, sizeof(*buffer));
|
||||||
|
if(buffer == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_buffer_init(&buffer->base, &msg_buffer_impl, width, height);
|
||||||
|
buffer->format = DRM_FORMAT_ARGB8888;
|
||||||
|
buffer->stride = stride;
|
||||||
|
|
||||||
|
buffer->data = malloc(buffer->stride * height);
|
||||||
|
if(buffer->data == NULL) {
|
||||||
|
free(buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
cairo_subpixel_order_t
|
cairo_subpixel_order_t
|
||||||
to_cairo_subpixel_order(const enum wl_output_subpixel subpixel) {
|
to_cairo_subpixel_order(const enum wl_output_subpixel subpixel) {
|
||||||
switch(subpixel) {
|
switch(subpixel) {
|
||||||
|
|
@ -31,12 +101,11 @@ to_cairo_subpixel_order(const enum wl_output_subpixel subpixel) {
|
||||||
return CAIRO_SUBPIXEL_ORDER_DEFAULT;
|
return CAIRO_SUBPIXEL_ORDER_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_texture *
|
struct msg_buffer *
|
||||||
create_message_texture(const char *string, const struct cg_output *output) {
|
create_message_texture(const char *string, const struct cg_output *output) {
|
||||||
const int WIDTH_PADDING = 8;
|
const int WIDTH_PADDING = 8;
|
||||||
const int HEIGHT_PADDING = 2;
|
const int HEIGHT_PADDING = 2;
|
||||||
|
|
||||||
struct wlr_texture *texture;
|
|
||||||
double scale = output->wlr_output->scale;
|
double scale = output->wlr_output->scale;
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
|
|
@ -75,7 +144,6 @@ create_message_texture(const char *string, const struct cg_output *output) {
|
||||||
float *bg_col = output->server->message_config.bg_color;
|
float *bg_col = output->server->message_config.bg_color;
|
||||||
cairo_set_source_rgba(cairo, bg_col[0], bg_col[1], bg_col[2], bg_col[3]);
|
cairo_set_source_rgba(cairo, bg_col[0], bg_col[1], bg_col[2], bg_col[3]);
|
||||||
cairo_paint(cairo);
|
cairo_paint(cairo);
|
||||||
PangoContext *pango = pango_cairo_create_context(cairo);
|
|
||||||
float *fg_col = output->server->message_config.fg_color;
|
float *fg_col = output->server->message_config.fg_color;
|
||||||
cairo_set_source_rgba(cairo, fg_col[0], fg_col[1], fg_col[2], fg_col[3]);
|
cairo_set_source_rgba(cairo, fg_col[0], fg_col[1], fg_col[2], fg_col[3]);
|
||||||
cairo_set_line_width(cairo, 2);
|
cairo_set_line_width(cairo, 2);
|
||||||
|
|
@ -89,13 +157,22 @@ create_message_texture(const char *string, const struct cg_output *output) {
|
||||||
cairo_surface_flush(surface);
|
cairo_surface_flush(surface);
|
||||||
unsigned char *data = cairo_image_surface_get_data(surface);
|
unsigned char *data = cairo_image_surface_get_data(surface);
|
||||||
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
|
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
|
||||||
texture =
|
|
||||||
wlr_texture_from_pixels(output->server->renderer, DRM_FORMAT_ARGB8888,
|
struct msg_buffer *buf = msg_buffer_create(width, height, stride);
|
||||||
stride, width, height, data);
|
void *data_ptr;
|
||||||
|
|
||||||
|
if(!wlr_buffer_begin_data_ptr_access(&buf->base,
|
||||||
|
WLR_BUFFER_DATA_PTR_ACCESS_WRITE,
|
||||||
|
&data_ptr, NULL, NULL)) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to get pointer access to message buffer");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy(data_ptr, data, stride * height);
|
||||||
|
wlr_buffer_end_data_ptr_access(&buf->base);
|
||||||
|
|
||||||
cairo_surface_destroy(surface);
|
cairo_surface_destroy(surface);
|
||||||
g_object_unref(pango);
|
|
||||||
cairo_destroy(cairo);
|
cairo_destroy(cairo);
|
||||||
return texture;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CG_HAS_FANALYZE
|
#if CG_HAS_FANALYZE
|
||||||
|
|
@ -111,8 +188,8 @@ message_set_output(struct cg_output *output, const char *string,
|
||||||
free(box);
|
free(box);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
message->message = create_message_texture(string, output);
|
struct msg_buffer *buf = create_message_texture(string, output);
|
||||||
if(!message->message) {
|
if(!buf) {
|
||||||
wlr_log(WLR_ERROR, "Could not create message texture");
|
wlr_log(WLR_ERROR, "Could not create message texture");
|
||||||
free(box);
|
free(box);
|
||||||
free(message);
|
free(message);
|
||||||
|
|
@ -121,8 +198,9 @@ message_set_output(struct cg_output *output, const char *string,
|
||||||
message->position = box;
|
message->position = box;
|
||||||
wl_list_insert(&output->messages, &message->link);
|
wl_list_insert(&output->messages, &message->link);
|
||||||
|
|
||||||
int width = message->message->width;
|
double scale = output->wlr_output->scale;
|
||||||
int height = message->message->height;
|
int width = buf->base.width / scale;
|
||||||
|
int height = buf->base.height / scale;
|
||||||
message->position->width = width;
|
message->position->width = width;
|
||||||
message->position->height = height;
|
message->position->height = height;
|
||||||
switch(align) {
|
switch(align) {
|
||||||
|
|
@ -148,7 +226,23 @@ message_set_output(struct cg_output *output, const char *string,
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
wlr_output_damage_add_box(output->damage, message->position);
|
|
||||||
|
struct wlr_scene_output *scene_output =
|
||||||
|
wlr_scene_get_scene_output(output->server->scene, output->wlr_output);
|
||||||
|
if(scene_output == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
message->message =
|
||||||
|
wlr_scene_buffer_create(&scene_output->scene->tree, &buf->base);
|
||||||
|
wlr_scene_node_raise_to_top(&message->message->node);
|
||||||
|
wlr_scene_node_set_enabled(&message->message->node, true);
|
||||||
|
struct wlr_box outp_box;
|
||||||
|
wlr_output_layout_get_box(output->server->output_layout, output->wlr_output,
|
||||||
|
&outp_box);
|
||||||
|
wlr_scene_buffer_set_dest_size(message->message, width, height);
|
||||||
|
wlr_scene_node_set_position(&message->message->node,
|
||||||
|
message->position->x + outp_box.x,
|
||||||
|
message->position->y + outp_box.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -168,10 +262,11 @@ message_printf(struct cg_output *output, const char *fmt, ...) {
|
||||||
free(buffer);
|
free(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct wlr_box *output_box = wlr_output_layout_get_box(
|
struct wlr_box output_box;
|
||||||
output->server->output_layout, output->wlr_output);
|
wlr_output_layout_get_box(output->server->output_layout, output->wlr_output,
|
||||||
|
&output_box);
|
||||||
|
|
||||||
box->x = output_box->width;
|
box->x = output_box.width;
|
||||||
box->y = 0;
|
box->y = 0;
|
||||||
box->width = 0;
|
box->width = 0;
|
||||||
box->height = 0;
|
box->height = 0;
|
||||||
|
|
@ -205,9 +300,10 @@ message_clear(struct cg_output *output) {
|
||||||
struct cg_message *message, *tmp;
|
struct cg_message *message, *tmp;
|
||||||
wl_list_for_each_safe(message, tmp, &output->messages, link) {
|
wl_list_for_each_safe(message, tmp, &output->messages, link) {
|
||||||
wl_list_remove(&message->link);
|
wl_list_remove(&message->link);
|
||||||
wlr_output_damage_add_box(output->damage, message->position);
|
struct wlr_buffer *buf = message->message->buffer;
|
||||||
wlr_texture_destroy(message->message);
|
wlr_scene_node_destroy(&message->message->node);
|
||||||
free(message->position);
|
free(message->position);
|
||||||
|
buf->impl->destroy(buf);
|
||||||
free(message);
|
free(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef MESSAGE_H
|
#ifndef MESSAGE_H
|
||||||
|
|
||||||
#define MESSAGE_H
|
#define MESSAGE_H
|
||||||
|
|
@ -6,7 +9,7 @@
|
||||||
|
|
||||||
struct cg_output;
|
struct cg_output;
|
||||||
struct wlr_box;
|
struct wlr_box;
|
||||||
struct wlr_texture;
|
struct wlr_buffer;
|
||||||
|
|
||||||
enum cg_message_align {
|
enum cg_message_align {
|
||||||
CG_MESSAGE_TOP_LEFT,
|
CG_MESSAGE_TOP_LEFT,
|
||||||
|
|
@ -25,7 +28,8 @@ struct cg_message_config {
|
||||||
|
|
||||||
struct cg_message {
|
struct cg_message {
|
||||||
struct wlr_box *position;
|
struct wlr_box *position;
|
||||||
struct wlr_texture *message;
|
struct wlr_scene_buffer *message;
|
||||||
|
struct wl_surface *surface;
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
33
output.h
33
output.h
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef CG_OUTPUT_H
|
#ifndef CG_OUTPUT_H
|
||||||
#define CG_OUTPUT_H
|
#define CG_OUTPUT_H
|
||||||
|
|
||||||
|
|
@ -7,19 +10,17 @@
|
||||||
struct cg_server;
|
struct cg_server;
|
||||||
struct cg_view;
|
struct cg_view;
|
||||||
struct wlr_output;
|
struct wlr_output;
|
||||||
struct wlr_output_damage;
|
|
||||||
struct wlr_surface;
|
struct wlr_surface;
|
||||||
|
|
||||||
struct cg_output {
|
struct cg_output {
|
||||||
struct cg_server *server;
|
struct cg_server *server;
|
||||||
struct wlr_output *wlr_output;
|
struct wlr_output *wlr_output;
|
||||||
struct wlr_output_damage *damage;
|
struct wlr_scene_rect *bg;
|
||||||
|
|
||||||
struct wl_listener mode;
|
struct wl_listener mode;
|
||||||
struct wl_listener commit;
|
struct wl_listener commit;
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
struct wl_listener damage_frame;
|
struct wl_listener frame;
|
||||||
struct wl_listener damage_destroy;
|
|
||||||
struct cg_workspace **workspaces;
|
struct cg_workspace **workspaces;
|
||||||
struct wl_list messages;
|
struct wl_list messages;
|
||||||
int curr_workspace;
|
int curr_workspace;
|
||||||
|
|
@ -42,7 +43,9 @@ struct cg_output_config {
|
||||||
struct wlr_box pos;
|
struct wlr_box pos;
|
||||||
char *output_name;
|
char *output_name;
|
||||||
float refresh_rate;
|
float refresh_rate;
|
||||||
|
float scale;
|
||||||
int priority;
|
int priority;
|
||||||
|
int angle; // enum wl_output_transform, -1 signifies "unspecified"
|
||||||
struct wl_list link; // cg_server::output_config
|
struct wl_list link; // cg_server::output_config
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -56,23 +59,9 @@ handle_new_output(struct wl_listener *listener, void *data);
|
||||||
void
|
void
|
||||||
output_configure(struct cg_server *server, struct cg_output *output);
|
output_configure(struct cg_server *server, struct cg_output *output);
|
||||||
void
|
void
|
||||||
output_surface_for_each_surface(struct cg_output *output,
|
|
||||||
struct wlr_surface *surface, double ox,
|
|
||||||
double oy, cg_surface_iterator_func_t iterator,
|
|
||||||
void *user_data);
|
|
||||||
void
|
|
||||||
output_view_for_each_popup(struct cg_output *output, struct cg_view *view,
|
|
||||||
cg_surface_iterator_func_t iterator,
|
|
||||||
void *user_data);
|
|
||||||
void
|
|
||||||
output_drag_icons_for_each_surface(struct cg_output *output,
|
|
||||||
struct wl_list *drag_icons,
|
|
||||||
cg_surface_iterator_func_t iterator,
|
|
||||||
void *user_data);
|
|
||||||
void
|
|
||||||
output_damage_surface(struct cg_output *output, struct wlr_surface *surface,
|
|
||||||
double ox, double oy, bool whole);
|
|
||||||
void
|
|
||||||
output_set_window_title(struct cg_output *output, const char *title);
|
output_set_window_title(struct cg_output *output, const char *title);
|
||||||
|
void
|
||||||
|
output_make_workspace_fullscreen(struct cg_output *output, int ws);
|
||||||
|
int
|
||||||
|
output_get_num(const struct cg_output *output);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
3
pango.c
3
pango.c
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <pango/pangocairo.h>
|
#include <pango/pangocairo.h>
|
||||||
|
|
|
||||||
3
pango.h
3
pango.h
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef _SWAY_PANGO_H
|
#ifndef _SWAY_PANGO_H
|
||||||
#define _SWAY_PANGO_H
|
#define _SWAY_PANGO_H
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
|
|
|
||||||
201
parse.c
201
parse.c
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200812L
|
#define _POSIX_C_SOURCE 200812L
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
@ -75,12 +78,13 @@ parse_key(struct keybinding *keybinding, const char *key_def, char **errstr) {
|
||||||
|
|
||||||
int
|
int
|
||||||
parse_command(struct cg_server *server, struct keybinding *keybinding,
|
parse_command(struct cg_server *server, struct keybinding *keybinding,
|
||||||
char *saveptr, char **errstr);
|
char *saveptr, char **errstr, int nesting_level);
|
||||||
|
|
||||||
/* Parse a keybinding definition and return it if successful, else return NULL
|
/* Parse a keybinding definition and return it if successful, else return NULL
|
||||||
*/
|
*/
|
||||||
struct keybinding *
|
struct keybinding *
|
||||||
parse_keybinding(struct cg_server *server, char **saveptr, char **errstr) {
|
parse_keybinding(struct cg_server *server, char **saveptr, char **errstr,
|
||||||
|
int nesting_level) {
|
||||||
struct keybinding *keybinding = malloc(sizeof(struct keybinding));
|
struct keybinding *keybinding = malloc(sizeof(struct keybinding));
|
||||||
if(keybinding == NULL) {
|
if(keybinding == NULL) {
|
||||||
*errstr = log_error(
|
*errstr = log_error(
|
||||||
|
|
@ -93,7 +97,8 @@ parse_keybinding(struct cg_server *server, char **saveptr, char **errstr) {
|
||||||
free(keybinding);
|
free(keybinding);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if(parse_command(server, keybinding, *saveptr, errstr) != 0) {
|
if(parse_command(server, keybinding, *saveptr, errstr, nesting_level + 1) !=
|
||||||
|
0) {
|
||||||
free(keybinding);
|
free(keybinding);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -116,9 +121,28 @@ parse_float(char **saveptr, const char *delim) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
parse_uint(char **saveptr, const char *delim) {
|
||||||
|
char *uint_str = strtok_r(NULL, delim, saveptr);
|
||||||
|
if(uint_str == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Expected a non-negative integer, got nothing");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
long uint = strtol(uint_str, NULL, 10);
|
||||||
|
if(uint >= 0 && uint <= INT_MAX) {
|
||||||
|
return uint;
|
||||||
|
} else {
|
||||||
|
wlr_log(WLR_ERROR,
|
||||||
|
"Error parsing non-negative integer. Must be a number larger "
|
||||||
|
"or equal to 0 and less or equal to %d",
|
||||||
|
INT_MAX);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct cg_input_config *
|
struct cg_input_config *
|
||||||
parse_input_config(char **saveptr, char **errstr) {
|
parse_input_config(char **saveptr, char **errstr) {
|
||||||
struct cg_input_config *cfg = calloc(1, sizeof(struct cg_input_config));
|
struct cg_input_config *cfg = input_manager_create_empty_input_config();
|
||||||
char *value = NULL;
|
char *value = NULL;
|
||||||
char *ident = NULL;
|
char *ident = NULL;
|
||||||
if(cfg == NULL) {
|
if(cfg == NULL) {
|
||||||
|
|
@ -127,28 +151,6 @@ parse_input_config(char **saveptr, char **errstr) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg->tap = INT_MIN;
|
|
||||||
cfg->tap_button_map = INT_MIN;
|
|
||||||
cfg->drag = INT_MIN;
|
|
||||||
cfg->drag_lock = INT_MIN;
|
|
||||||
cfg->dwt = INT_MIN;
|
|
||||||
cfg->send_events = INT_MIN;
|
|
||||||
cfg->click_method = INT_MIN;
|
|
||||||
cfg->middle_emulation = INT_MIN;
|
|
||||||
cfg->natural_scroll = INT_MIN;
|
|
||||||
cfg->accel_profile = INT_MIN;
|
|
||||||
cfg->pointer_accel = FLT_MIN;
|
|
||||||
cfg->scroll_factor = FLT_MIN;
|
|
||||||
cfg->scroll_button = INT_MIN;
|
|
||||||
cfg->scroll_method = INT_MIN;
|
|
||||||
cfg->left_handed = INT_MIN;
|
|
||||||
/*cfg->repeat_delay = INT_MIN;
|
|
||||||
cfg->repeat_rate = INT_MIN;
|
|
||||||
cfg->xkb_numlock = INT_MIN;
|
|
||||||
cfg->xkb_capslock = INT_MIN;
|
|
||||||
cfg->xkb_file_is_set = false;
|
|
||||||
wl_list_init(&cfg->tools);*/
|
|
||||||
|
|
||||||
ident = strtok_r(NULL, " ", saveptr);
|
ident = strtok_r(NULL, " ", saveptr);
|
||||||
|
|
||||||
if(ident == NULL) {
|
if(ident == NULL) {
|
||||||
|
|
@ -341,6 +343,32 @@ parse_input_config(char **saveptr, char **errstr) {
|
||||||
"Invalid option \"%s\" to setting \"tap_button_map\"", value);
|
"Invalid option \"%s\" to setting \"tap_button_map\"", value);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
} else if(strcmp(setting, "keybindings") == 0) {
|
||||||
|
if(strcmp(value, "enabled") == 0) {
|
||||||
|
cfg->enable_keybindings = true;
|
||||||
|
} else if(strcmp(value, "disabled") == 0) {
|
||||||
|
cfg->enable_keybindings = false;
|
||||||
|
} else {
|
||||||
|
*errstr = log_error(
|
||||||
|
"Invalid option \"%s\" to setting \"keybindings\"", value);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else if(strcmp(setting, "repeat_delay") == 0) {
|
||||||
|
cfg->repeat_delay = parse_uint(saveptr, " ");
|
||||||
|
if(cfg->repeat_delay < 0) {
|
||||||
|
*errstr = log_error("Invalid option \"%s\" to setting "
|
||||||
|
"\"repeat_delay\", expected positive integer",
|
||||||
|
value);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else if(strcmp(setting, "repeat_rate") == 0) {
|
||||||
|
cfg->repeat_rate = parse_uint(saveptr, " ");
|
||||||
|
if(cfg->repeat_rate < 0) {
|
||||||
|
*errstr = log_error("Invalid option \"%s\" to setting "
|
||||||
|
"\"repeat_rate\", expected positive integer",
|
||||||
|
value);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*errstr = log_error("Invalid option to command \"input\"");
|
*errstr = log_error("Invalid option to command \"input\"");
|
||||||
goto error;
|
goto error;
|
||||||
|
|
@ -365,8 +393,10 @@ error:
|
||||||
}
|
}
|
||||||
|
|
||||||
struct keybinding *
|
struct keybinding *
|
||||||
parse_bind(struct cg_server *server, char **saveptr, char **errstr) {
|
parse_bind(struct cg_server *server, char **saveptr, char **errstr,
|
||||||
struct keybinding *keybinding = parse_keybinding(server, saveptr, errstr);
|
int nesting_level) {
|
||||||
|
struct keybinding *keybinding =
|
||||||
|
parse_keybinding(server, saveptr, errstr, nesting_level);
|
||||||
if(keybinding == NULL) {
|
if(keybinding == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Could not parse keybinding for \"bind\".");
|
wlr_log(WLR_ERROR, "Could not parse keybinding for \"bind\".");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -376,7 +406,8 @@ parse_bind(struct cg_server *server, char **saveptr, char **errstr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct keybinding *
|
struct keybinding *
|
||||||
parse_definekey(struct cg_server *server, char **saveptr, char **errstr) {
|
parse_definekey(struct cg_server *server, char **saveptr, char **errstr,
|
||||||
|
int nesting_level) {
|
||||||
char *mode = strtok_r(NULL, " ", saveptr);
|
char *mode = strtok_r(NULL, " ", saveptr);
|
||||||
if(mode == NULL) {
|
if(mode == NULL) {
|
||||||
*errstr =
|
*errstr =
|
||||||
|
|
@ -388,7 +419,8 @@ parse_definekey(struct cg_server *server, char **saveptr, char **errstr) {
|
||||||
*errstr = log_error("Unknown mode \"%s\"", mode);
|
*errstr = log_error("Unknown mode \"%s\"", mode);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
struct keybinding *keybinding = parse_keybinding(server, saveptr, errstr);
|
struct keybinding *keybinding =
|
||||||
|
parse_keybinding(server, saveptr, errstr, nesting_level);
|
||||||
if(keybinding == NULL) {
|
if(keybinding == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Could not parse keybinding for \"definekey\"");
|
wlr_log(WLR_ERROR, "Could not parse keybinding for \"definekey\"");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -454,6 +486,21 @@ parse_escape(char **saveptr, char **errstr) {
|
||||||
return keybinding;
|
return keybinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
parse_cursor(char **saveptr, char **errstr) {
|
||||||
|
if(strcmp(*saveptr, "enable") == 0) {
|
||||||
|
return 1;
|
||||||
|
} else if(strcmp(*saveptr, "disable") == 0) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
wlr_log(WLR_ERROR,
|
||||||
|
"Invalid option \"%s\" for \"cursor\". Expected \"enable\" or "
|
||||||
|
"\"disable\".",
|
||||||
|
*saveptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
parse_definemode(char **saveptr, char **errstr) {
|
parse_definemode(char **saveptr, char **errstr) {
|
||||||
char *mode = strtok_r(NULL, " ", saveptr);
|
char *mode = strtok_r(NULL, " ", saveptr);
|
||||||
|
|
@ -481,25 +528,6 @@ parse_workspaces(char **saveptr, char **errstr) {
|
||||||
return nws;
|
return nws;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
parse_uint(char **saveptr, const char *delim) {
|
|
||||||
char *uint_str = strtok_r(NULL, delim, saveptr);
|
|
||||||
if(uint_str == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Expected a non-negative integer, got nothing");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
long uint = strtol(uint_str, NULL, 10);
|
|
||||||
if(uint >= 0 && uint <= INT_MAX) {
|
|
||||||
return uint;
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_ERROR,
|
|
||||||
"Error parsing non-negative integer. Must be a number larger "
|
|
||||||
"or equal to 0 and less or equal to %d",
|
|
||||||
INT_MAX);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
parse_output_config_keyword(char *key_str, enum output_status *status) {
|
parse_output_config_keyword(char *key_str, enum output_status *status) {
|
||||||
if(key_str == NULL) {
|
if(key_str == NULL) {
|
||||||
|
|
@ -509,6 +537,10 @@ parse_output_config_keyword(char *key_str, enum output_status *status) {
|
||||||
*status = OUTPUT_DEFAULT;
|
*status = OUTPUT_DEFAULT;
|
||||||
} else if(strcmp(key_str, "prio") == 0) {
|
} else if(strcmp(key_str, "prio") == 0) {
|
||||||
*status = OUTPUT_DEFAULT;
|
*status = OUTPUT_DEFAULT;
|
||||||
|
} else if(strcmp(key_str, "rotate") == 0) {
|
||||||
|
*status = OUTPUT_DEFAULT;
|
||||||
|
} else if(strcmp(key_str, "scale") == 0) {
|
||||||
|
*status = OUTPUT_DEFAULT;
|
||||||
} else if(strcmp(key_str, "enable") == 0) {
|
} else if(strcmp(key_str, "enable") == 0) {
|
||||||
*status = OUTPUT_ENABLE;
|
*status = OUTPUT_ENABLE;
|
||||||
} else if(strcmp(key_str, "disable") == 0) {
|
} else if(strcmp(key_str, "disable") == 0) {
|
||||||
|
|
@ -532,6 +564,8 @@ parse_output_config(char **saveptr, char **errstr) {
|
||||||
cfg->output_name = NULL;
|
cfg->output_name = NULL;
|
||||||
cfg->refresh_rate = 0;
|
cfg->refresh_rate = 0;
|
||||||
cfg->priority = -1;
|
cfg->priority = -1;
|
||||||
|
cfg->scale = -1;
|
||||||
|
cfg->angle = -1;
|
||||||
char *name = strtok_r(NULL, " ", saveptr);
|
char *name = strtok_r(NULL, " ", saveptr);
|
||||||
if(name == NULL) {
|
if(name == NULL) {
|
||||||
*errstr =
|
*errstr =
|
||||||
|
|
@ -550,6 +584,19 @@ parse_output_config(char **saveptr, char **errstr) {
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(strcmp(key_str, "rotate") == 0) {
|
||||||
|
uint32_t angle = parse_uint(saveptr, " ");
|
||||||
|
// 360 degrees is equivalent to 0 degrees
|
||||||
|
cfg->angle = angle % 4;
|
||||||
|
if(cfg->angle < 0) {
|
||||||
|
*errstr =
|
||||||
|
log_error("Error parsing option rotate for output %s", name);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
cfg->output_name = strdup(name);
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
if(strcmp(key_str, "prio") == 0) {
|
if(strcmp(key_str, "prio") == 0) {
|
||||||
cfg->priority = parse_uint(saveptr, " ");
|
cfg->priority = parse_uint(saveptr, " ");
|
||||||
if(cfg->priority < 0) {
|
if(cfg->priority < 0) {
|
||||||
|
|
@ -562,6 +609,19 @@ parse_output_config(char **saveptr, char **errstr) {
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(strcmp(key_str, "scale") == 0) {
|
||||||
|
cfg->scale = parse_float(saveptr, " ");
|
||||||
|
if(cfg->scale <= 0.0) {
|
||||||
|
*errstr =
|
||||||
|
log_error("Error parsing scale of output configuration for "
|
||||||
|
"output %s, expected positive float",
|
||||||
|
name);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
cfg->output_name = strdup(name);
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
cfg->pos.x = parse_uint(saveptr, " ");
|
cfg->pos.x = parse_uint(saveptr, " ");
|
||||||
if(cfg->pos.x < 0) {
|
if(cfg->pos.x < 0) {
|
||||||
*errstr = log_error(
|
*errstr = log_error(
|
||||||
|
|
@ -692,6 +752,9 @@ parse_message_config(char **saveptr, char **errstr) {
|
||||||
return cfg;
|
return cfg;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
if(cfg != NULL) {
|
||||||
|
free(cfg);
|
||||||
|
}
|
||||||
wlr_log(WLR_ERROR, "Message configuration must be of the form "
|
wlr_log(WLR_ERROR, "Message configuration must be of the form "
|
||||||
"'configure_message <setting> <value>'");
|
"'configure_message <setting> <value>'");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -699,8 +762,13 @@ error:
|
||||||
|
|
||||||
int
|
int
|
||||||
parse_command(struct cg_server *server, struct keybinding *keybinding,
|
parse_command(struct cg_server *server, struct keybinding *keybinding,
|
||||||
char *saveptr, char **errstr) {
|
char *saveptr, char **errstr, int nesting_level) {
|
||||||
char *action = strtok_r(NULL, " ", &saveptr);
|
char *action = strtok_r(NULL, " ", &saveptr);
|
||||||
|
if(nesting_level >= MAX_NESTING_LEVEL) {
|
||||||
|
*errstr =
|
||||||
|
log_error("Nesting level of commands is too deep. Giving up.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if(action == NULL) {
|
if(action == NULL) {
|
||||||
*errstr = log_error("Expexted an action to parse, got none.");
|
*errstr = log_error("Expexted an action to parse, got none.");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -712,10 +780,29 @@ parse_command(struct cg_server *server, struct keybinding *keybinding,
|
||||||
keybinding->action = KEYBINDING_SPLIT_HORIZONTAL;
|
keybinding->action = KEYBINDING_SPLIT_HORIZONTAL;
|
||||||
} else if(strcmp(action, "quit") == 0) {
|
} else if(strcmp(action, "quit") == 0) {
|
||||||
keybinding->action = KEYBINDING_QUIT;
|
keybinding->action = KEYBINDING_QUIT;
|
||||||
|
} else if(strcmp(action, "dump") == 0) {
|
||||||
|
keybinding->action = KEYBINDING_DUMP;
|
||||||
} else if(strcmp(action, "show_info") == 0) {
|
} else if(strcmp(action, "show_info") == 0) {
|
||||||
keybinding->action = KEYBINDING_SHOW_INFO;
|
keybinding->action = KEYBINDING_SHOW_INFO;
|
||||||
} else if(strcmp(action, "close") == 0) {
|
} else if(strcmp(action, "close") == 0) {
|
||||||
keybinding->action = KEYBINDING_CLOSE_VIEW;
|
keybinding->action = KEYBINDING_CLOSE_VIEW;
|
||||||
|
} else if(strcmp(action, "focus_tile") == 0) {
|
||||||
|
keybinding->action = KEYBINDING_FOCUS_TILE;
|
||||||
|
char *nws_str = strtok_r(NULL, " ", &saveptr);
|
||||||
|
if(nws_str == NULL) {
|
||||||
|
*errstr = log_error(
|
||||||
|
"Expected argument for \"focus_tile\" action, got none.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
long tile_id = strtol(nws_str, NULL, 10);
|
||||||
|
if(tile_id < 1) {
|
||||||
|
*errstr = log_error(
|
||||||
|
"Tile id for focus_tile must be a positive integer. Got %l\n",
|
||||||
|
tile_id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
keybinding->data.u = tile_id;
|
||||||
} else if(strcmp(action, "focus") == 0) {
|
} else if(strcmp(action, "focus") == 0) {
|
||||||
keybinding->action = KEYBINDING_CYCLE_TILES;
|
keybinding->action = KEYBINDING_CYCLE_TILES;
|
||||||
keybinding->data.b = false;
|
keybinding->data.b = false;
|
||||||
|
|
@ -899,13 +986,15 @@ parse_command(struct cg_server *server, struct keybinding *keybinding,
|
||||||
keybinding->data.u = (unsigned int)mode_idx;
|
keybinding->data.u = (unsigned int)mode_idx;
|
||||||
} else if(strcmp(action, "bind") == 0) {
|
} else if(strcmp(action, "bind") == 0) {
|
||||||
keybinding->action = KEYBINDING_DEFINEKEY;
|
keybinding->action = KEYBINDING_DEFINEKEY;
|
||||||
keybinding->data.kb = parse_bind(server, &saveptr, errstr);
|
keybinding->data.kb =
|
||||||
|
parse_bind(server, &saveptr, errstr, nesting_level);
|
||||||
if(keybinding->data.kb == NULL) {
|
if(keybinding->data.kb == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if(strcmp(action, "definekey") == 0) {
|
} else if(strcmp(action, "definekey") == 0) {
|
||||||
keybinding->action = KEYBINDING_DEFINEKEY;
|
keybinding->action = KEYBINDING_DEFINEKEY;
|
||||||
keybinding->data.kb = parse_definekey(server, &saveptr, errstr);
|
keybinding->data.kb =
|
||||||
|
parse_definekey(server, &saveptr, errstr, nesting_level);
|
||||||
if(keybinding->data.kb == NULL) {
|
if(keybinding->data.kb == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -921,6 +1010,12 @@ parse_command(struct cg_server *server, struct keybinding *keybinding,
|
||||||
if(keybinding->data.kb == NULL) {
|
if(keybinding->data.kb == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
} else if(strcmp(action, "cursor") == 0) {
|
||||||
|
keybinding->action = KEYBINDING_CURSOR;
|
||||||
|
keybinding->data.i = parse_cursor(&saveptr, errstr);
|
||||||
|
if(keybinding->data.i < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
} else if(strcmp(action, "definemode") == 0) {
|
} else if(strcmp(action, "definemode") == 0) {
|
||||||
keybinding->action = KEYBINDING_DEFINEMODE;
|
keybinding->action = KEYBINDING_DEFINEMODE;
|
||||||
keybinding->data.c = parse_definemode(&saveptr, errstr);
|
keybinding->data.c = parse_definemode(&saveptr, errstr);
|
||||||
|
|
@ -970,7 +1065,7 @@ parse_rc_line(struct cg_server *server, char *line, char **errstr) {
|
||||||
free(saveptr);
|
free(saveptr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(parse_command(server, keybinding, saveptr, errstr) != 0) {
|
if(parse_command(server, keybinding, saveptr, errstr, 1) != 0) {
|
||||||
wlr_log(WLR_ERROR, "Error parsing command.");
|
wlr_log(WLR_ERROR, "Error parsing command.");
|
||||||
free(keybinding);
|
free(keybinding);
|
||||||
free(saveptr);
|
free(saveptr);
|
||||||
|
|
|
||||||
5
parse.h
5
parse.h
|
|
@ -1,9 +1,10 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef PARSE_H
|
#ifndef PARSE_H
|
||||||
|
|
||||||
#define PARSE_H
|
#define PARSE_H
|
||||||
|
|
||||||
#define MAX_LINE_SIZE 256
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
struct cg_server;
|
struct cg_server;
|
||||||
|
|
|
||||||
274
render.c
274
render.c
|
|
@ -1,274 +0,0 @@
|
||||||
/*
|
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2022 The Cagebreak Authors
|
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
|
||||||
* Copyright (C) 2019 The Sway authors
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <wayland-server-core.h>
|
|
||||||
#include <wlr/backend.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
|
||||||
#include <wlr/types/wlr_matrix.h>
|
|
||||||
#include <wlr/types/wlr_output.h>
|
|
||||||
#include <wlr/types/wlr_output_damage.h>
|
|
||||||
#include <wlr/types/wlr_surface.h>
|
|
||||||
#include <wlr/util/box.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include <wlr/util/region.h>
|
|
||||||
|
|
||||||
#include "message.h"
|
|
||||||
#include "output.h"
|
|
||||||
#include "seat.h"
|
|
||||||
#include "server.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "view.h"
|
|
||||||
#include "workspace.h"
|
|
||||||
|
|
||||||
static void
|
|
||||||
scissor_output(struct wlr_output *output, pixman_box32_t *rect,
|
|
||||||
struct wlr_renderer *renderer) {
|
|
||||||
|
|
||||||
struct wlr_box box = {
|
|
||||||
.x = rect->x1,
|
|
||||||
.y = rect->y1,
|
|
||||||
.width = rect->x2 - rect->x1,
|
|
||||||
.height = rect->y2 - rect->y1,
|
|
||||||
};
|
|
||||||
|
|
||||||
int output_width, output_height;
|
|
||||||
wlr_output_transformed_resolution(output, &output_width, &output_height);
|
|
||||||
enum wl_output_transform transform =
|
|
||||||
wlr_output_transform_invert(output->transform);
|
|
||||||
wlr_box_transform(&box, &box, transform, output_width, output_height);
|
|
||||||
|
|
||||||
wlr_renderer_scissor(renderer, &box);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct render_data {
|
|
||||||
pixman_region32_t *damage;
|
|
||||||
int tile_width, tile_height;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
render_texture(struct wlr_output *wlr_output, pixman_region32_t *output_damage,
|
|
||||||
struct wlr_texture *texture, const struct wlr_box *box,
|
|
||||||
const float matrix[static 9], struct wlr_renderer *renderer) {
|
|
||||||
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, output_damage);
|
|
||||||
if(!pixman_region32_not_empty(&damage)) {
|
|
||||||
goto damage_finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nrects;
|
|
||||||
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
|
|
||||||
for(int i = 0; i < nrects; i++) {
|
|
||||||
scissor_output(wlr_output, &rects[i], renderer);
|
|
||||||
wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0F);
|
|
||||||
}
|
|
||||||
|
|
||||||
damage_finish:
|
|
||||||
pixman_region32_fini(&damage);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
render_surface_iterator(struct cg_output *output, struct wlr_surface *surface,
|
|
||||||
struct wlr_box *box, void *user_data) {
|
|
||||||
struct render_data *data = user_data;
|
|
||||||
struct wlr_output *wlr_output = output->wlr_output;
|
|
||||||
pixman_region32_t *output_damage = data->damage;
|
|
||||||
|
|
||||||
struct wlr_texture *texture = wlr_surface_get_texture(surface);
|
|
||||||
if(!texture) {
|
|
||||||
wlr_log(WLR_DEBUG, "Cannot obtain surface texture");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scale_box(box, 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.0F,
|
|
||||||
wlr_output->transform_matrix);
|
|
||||||
|
|
||||||
if(data->tile_width != 0) {
|
|
||||||
box->width =
|
|
||||||
box->width > data->tile_width ? data->tile_width : box->width;
|
|
||||||
}
|
|
||||||
if(data->tile_height != 0) {
|
|
||||||
box->height =
|
|
||||||
box->height > data->tile_height ? data->tile_height : box->height;
|
|
||||||
}
|
|
||||||
render_texture(wlr_output, output_damage, texture, box, matrix,
|
|
||||||
output->server->renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
render_drag_icons(struct cg_output *output, pixman_region32_t *damage,
|
|
||||||
struct wl_list *drag_icons) {
|
|
||||||
struct render_data data = {
|
|
||||||
.damage = damage,
|
|
||||||
.tile_width = 0,
|
|
||||||
.tile_height = 0,
|
|
||||||
};
|
|
||||||
output_drag_icons_for_each_surface(output, drag_icons,
|
|
||||||
render_surface_iterator, &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render all toplevels without descending into popups.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
render_view_toplevels(struct cg_view *view, struct cg_output *output,
|
|
||||||
pixman_region32_t *damage) {
|
|
||||||
struct render_data data = {
|
|
||||||
.damage = damage,
|
|
||||||
.tile_width = 0,
|
|
||||||
.tile_height = 0,
|
|
||||||
};
|
|
||||||
struct cg_tile *view_tile = view_get_tile(view);
|
|
||||||
if(view_tile != NULL) {
|
|
||||||
if(view_tile->tile.width != view->wlr_surface->current.width ||
|
|
||||||
view_tile->tile.height != view->wlr_surface->current.height) {
|
|
||||||
view_damage_whole(view);
|
|
||||||
}
|
|
||||||
data.tile_width = view_tile->tile.width;
|
|
||||||
data.tile_height = view_tile->tile.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
double ox, oy;
|
|
||||||
ox = view->ox;
|
|
||||||
oy = view->oy;
|
|
||||||
output_surface_for_each_surface(output, view->wlr_surface, ox, oy,
|
|
||||||
render_surface_iterator, &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
render_popup_iterator(struct cg_output *output, struct wlr_surface *surface,
|
|
||||||
struct wlr_box *box, void *data) {
|
|
||||||
/* Render this popup's surface. */
|
|
||||||
render_surface_iterator(output, surface, box, data);
|
|
||||||
|
|
||||||
/* Render this popup's child toplevels. */
|
|
||||||
output_surface_for_each_surface(output, surface, box->x, box->y,
|
|
||||||
render_surface_iterator, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
render_view_popups(struct cg_view *view, struct cg_output *output,
|
|
||||||
pixman_region32_t *damage) {
|
|
||||||
struct render_data data = {
|
|
||||||
.damage = damage,
|
|
||||||
.tile_width = 0,
|
|
||||||
.tile_height = 0,
|
|
||||||
};
|
|
||||||
output_view_for_each_popup(output, view, render_popup_iterator, &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
output_render(struct cg_output *output, pixman_region32_t *damage) {
|
|
||||||
struct cg_server *server = output->server;
|
|
||||||
struct wlr_output *wlr_output = output->wlr_output;
|
|
||||||
|
|
||||||
struct wlr_renderer *renderer = output->server->renderer;
|
|
||||||
if(!renderer) {
|
|
||||||
wlr_log(WLR_DEBUG, "Expected the output backend to have a renderer");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
|
||||||
|
|
||||||
if(!pixman_region32_not_empty(damage)) {
|
|
||||||
wlr_log(WLR_DEBUG, "Output isn't damaged but needs a buffer swap");
|
|
||||||
goto renderer_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
if(server->debug_damage_tracking) {
|
|
||||||
wlr_renderer_clear(renderer, (float[]){1.0F, 0.0F, 0.0F, 1.0F});
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int nrects;
|
|
||||||
pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
|
|
||||||
for(int i = 0; i < nrects; i++) {
|
|
||||||
scissor_output(wlr_output, &rects[i], renderer);
|
|
||||||
wlr_renderer_clear(renderer, server->bg_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
for(struct cg_tile *tile =
|
|
||||||
output->workspaces[output->curr_workspace]->focused_tile;
|
|
||||||
first ||
|
|
||||||
tile != output->workspaces[output->curr_workspace]->focused_tile;
|
|
||||||
tile = tile->next) {
|
|
||||||
first = false;
|
|
||||||
/* Only render visible views */
|
|
||||||
if(tile->view != NULL) {
|
|
||||||
render_view_toplevels(tile->view, output, damage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cg_view *view;
|
|
||||||
wl_list_for_each_reverse(
|
|
||||||
view, &output->workspaces[output->curr_workspace]->unmanaged_views,
|
|
||||||
link) {
|
|
||||||
render_view_toplevels(view, output, damage);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cg_view *focused_view = seat_get_focus(server->seat);
|
|
||||||
if(focused_view != NULL) {
|
|
||||||
render_view_popups(focused_view, output, damage);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cg_message *message;
|
|
||||||
wl_list_for_each_reverse(message, &output->messages, link) {
|
|
||||||
float matrix[9];
|
|
||||||
wlr_matrix_project_box(matrix, message->position,
|
|
||||||
WL_OUTPUT_TRANSFORM_NORMAL, 0.0F,
|
|
||||||
wlr_output->transform_matrix);
|
|
||||||
render_texture(output->wlr_output, damage, message->message,
|
|
||||||
message->position, matrix, renderer);
|
|
||||||
}
|
|
||||||
render_drag_icons(output, damage, &server->seat->drag_icons);
|
|
||||||
|
|
||||||
renderer_end:
|
|
||||||
/* Draw software cursor in case hardware cursors aren't
|
|
||||||
available. This is a no-op when they are. */
|
|
||||||
wlr_output_render_software_cursors(wlr_output, damage);
|
|
||||||
wlr_renderer_scissor(renderer, NULL);
|
|
||||||
wlr_renderer_end(renderer);
|
|
||||||
|
|
||||||
int output_width, output_height;
|
|
||||||
wlr_output_transformed_resolution(wlr_output, &output_width,
|
|
||||||
&output_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,
|
|
||||||
output_width, output_height);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
if(server->debug_damage_tracking) {
|
|
||||||
pixman_region32_union_rect(&frame_damage, &frame_damage, 0, 0,
|
|
||||||
output_width, output_height);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
wlr_output_set_damage(wlr_output, &frame_damage);
|
|
||||||
pixman_region32_fini(&frame_damage);
|
|
||||||
|
|
||||||
if(!wlr_output_commit(wlr_output)) {
|
|
||||||
wlr_log(WLR_ERROR, "Could not commit output");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9
render.h
9
render.h
|
|
@ -1,9 +0,0 @@
|
||||||
#ifndef CG_RENDER_H
|
|
||||||
#define CG_RENDER_H
|
|
||||||
|
|
||||||
struct cg_output;
|
|
||||||
|
|
||||||
void
|
|
||||||
output_render(struct cg_output *output, pixman_region32_t *damage);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
440
seat.c
440
seat.c
|
|
@ -1,13 +1,7 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2022 The Cagebreak Authors
|
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200812L
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
|
@ -17,13 +11,14 @@
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wayland-server-protocol.h>
|
#include <wayland-server-protocol.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
|
#include <wlr/types/wlr_compositor.h>
|
||||||
#include <wlr/types/wlr_cursor.h>
|
#include <wlr/types/wlr_cursor.h>
|
||||||
#include <wlr/types/wlr_data_device.h>
|
#include <wlr/types/wlr_data_device.h>
|
||||||
#include <wlr/types/wlr_idle.h>
|
#include <wlr/types/wlr_idle.h>
|
||||||
#include <wlr/types/wlr_keyboard_group.h>
|
#include <wlr/types/wlr_keyboard_group.h>
|
||||||
#include <wlr/types/wlr_primary_selection.h>
|
#include <wlr/types/wlr_primary_selection.h>
|
||||||
|
#include <wlr/types/wlr_scene.h>
|
||||||
#include <wlr/types/wlr_seat.h>
|
#include <wlr/types/wlr_seat.h>
|
||||||
#include <wlr/types/wlr_surface.h>
|
|
||||||
#include <wlr/types/wlr_touch.h>
|
#include <wlr/types/wlr_touch.h>
|
||||||
#include <wlr/types/wlr_xcursor_manager.h>
|
#include <wlr/types/wlr_xcursor_manager.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
@ -46,69 +41,6 @@
|
||||||
static void
|
static void
|
||||||
drag_icon_update_position(struct cg_drag_icon *drag_icon);
|
drag_icon_update_position(struct cg_drag_icon *drag_icon);
|
||||||
|
|
||||||
/* XDG toplevels may have nested surfaces, such as popup windows for context
|
|
||||||
* menus or tooltips. This function tests if any of those are underneath the
|
|
||||||
* coordinates lx and ly (in output Layout Coordinates). If so, it sets the
|
|
||||||
* surface pointer to that wlr_surface and the sx and sy coordinates to the
|
|
||||||
* coordinates relative to that surface's top-left corner. */
|
|
||||||
static bool
|
|
||||||
view_at(const struct cg_view *view, double lx, double ly,
|
|
||||||
struct wlr_surface **surface, double *sx, double *sy) {
|
|
||||||
struct wlr_box *output_layout_box = wlr_output_layout_get_box(
|
|
||||||
view->server->output_layout, view->workspace->output->wlr_output);
|
|
||||||
if(output_layout_box == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
double view_sx = lx - view->ox - output_layout_box->x;
|
|
||||||
double view_sy = ly - view->oy - output_layout_box->y;
|
|
||||||
|
|
||||||
double _sx, _sy;
|
|
||||||
struct wlr_surface *_surface =
|
|
||||||
view_wlr_surface_at(view, view_sx, view_sy, &_sx, &_sy);
|
|
||||||
if(_surface != NULL) {
|
|
||||||
*sx = _sx;
|
|
||||||
*sy = _sy;
|
|
||||||
*surface = _surface;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If desktop_view_at returns a view, there is also a
|
|
||||||
* surface. There cannot be a surface without a view, either. It's
|
|
||||||
* both or nothing. */
|
|
||||||
static struct cg_view *
|
|
||||||
desktop_view_at(const struct cg_server *server, double lx, double ly,
|
|
||||||
struct wlr_surface **surface, double *sx, double *sy) {
|
|
||||||
struct cg_view *view;
|
|
||||||
wl_list_for_each(
|
|
||||||
view,
|
|
||||||
&server->curr_output->workspaces[server->curr_output->curr_workspace]
|
|
||||||
->unmanaged_views,
|
|
||||||
link) {
|
|
||||||
if(view_at(view, lx, ly, surface, sx, sy)) {
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
for(struct cg_tile *tile =
|
|
||||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
|
||||||
->focused_tile;
|
|
||||||
first ||
|
|
||||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
|
||||||
->focused_tile != tile;
|
|
||||||
tile = tile->next) {
|
|
||||||
first = false;
|
|
||||||
if(tile->view != NULL && view_at(tile->view, lx, ly, surface, sx, sy)) {
|
|
||||||
return tile->view;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
update_capabilities(const struct cg_seat *seat) {
|
update_capabilities(const struct cg_seat *seat) {
|
||||||
uint32_t caps = 0;
|
uint32_t caps = 0;
|
||||||
|
|
@ -125,7 +57,8 @@ update_capabilities(const struct cg_seat *seat) {
|
||||||
wlr_seat_set_capabilities(seat->seat, caps);
|
wlr_seat_set_capabilities(seat->seat, caps);
|
||||||
|
|
||||||
/* Hide cursor if the seat doesn't have pointer capability. */
|
/* Hide cursor if the seat doesn't have pointer capability. */
|
||||||
if((caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
|
if(((caps & WL_SEAT_CAPABILITY_POINTER) == 0) ||
|
||||||
|
seat->enable_cursor == false) {
|
||||||
wlr_cursor_set_image(seat->cursor, NULL, 0, 0, 0, 0, 0, 0);
|
wlr_cursor_set_image(seat->cursor, NULL, 0, 0, 0, 0, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
wlr_xcursor_manager_set_cursor_image(seat->xcursor_manager,
|
wlr_xcursor_manager_set_cursor_image(seat->xcursor_manager,
|
||||||
|
|
@ -141,13 +74,12 @@ remove_touch(struct cg_seat *seat, struct cg_touch *touch) {
|
||||||
wlr_cursor_detach_input_device(seat->cursor, touch->device->wlr_device);
|
wlr_cursor_detach_input_device(seat->cursor, touch->device->wlr_device);
|
||||||
--seat->num_touch;
|
--seat->num_touch;
|
||||||
free(touch);
|
free(touch);
|
||||||
|
|
||||||
update_capabilities(seat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
new_touch(struct cg_seat *seat, struct cg_input_device *input_device) {
|
new_touch(struct cg_seat *seat, struct cg_input_device *input_device) {
|
||||||
struct wlr_input_device *device = input_device->wlr_device;
|
struct wlr_input_device *device = input_device->wlr_device;
|
||||||
|
struct wlr_touch *wlr_touch = wlr_touch_from_input_device(device);
|
||||||
struct cg_touch *touch = calloc(1, sizeof(struct cg_touch));
|
struct cg_touch *touch = calloc(1, sizeof(struct cg_touch));
|
||||||
if(!touch) {
|
if(!touch) {
|
||||||
wlr_log(WLR_ERROR, "Cannot allocate touch");
|
wlr_log(WLR_ERROR, "Cannot allocate touch");
|
||||||
|
|
@ -160,10 +92,10 @@ new_touch(struct cg_seat *seat, struct cg_input_device *input_device) {
|
||||||
input_device->touch = touch;
|
input_device->touch = touch;
|
||||||
++seat->num_touch;
|
++seat->num_touch;
|
||||||
|
|
||||||
if(device->output_name != NULL) {
|
if(wlr_touch->output_name != NULL) {
|
||||||
struct cg_output *output;
|
struct cg_output *output;
|
||||||
wl_list_for_each(output, &seat->server->outputs, link) {
|
wl_list_for_each(output, &seat->server->outputs, link) {
|
||||||
if(strcmp(device->output_name, output->wlr_output->name) == 0) {
|
if(strcmp(wlr_touch->output_name, output->wlr_output->name) == 0) {
|
||||||
wlr_cursor_map_input_to_output(seat->cursor, device,
|
wlr_cursor_map_input_to_output(seat->cursor, device,
|
||||||
output->wlr_output);
|
output->wlr_output);
|
||||||
break;
|
break;
|
||||||
|
|
@ -180,13 +112,12 @@ remove_pointer(struct cg_seat *seat, struct cg_pointer *pointer) {
|
||||||
wlr_cursor_detach_input_device(seat->cursor, pointer->device->wlr_device);
|
wlr_cursor_detach_input_device(seat->cursor, pointer->device->wlr_device);
|
||||||
--seat->num_pointers;
|
--seat->num_pointers;
|
||||||
free(pointer);
|
free(pointer);
|
||||||
|
|
||||||
update_capabilities(seat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
new_pointer(struct cg_seat *seat, struct cg_input_device *input_device) {
|
new_pointer(struct cg_seat *seat, struct cg_input_device *input_device) {
|
||||||
struct wlr_input_device *device = input_device->wlr_device;
|
struct wlr_input_device *device = input_device->wlr_device;
|
||||||
|
struct wlr_pointer *wlr_pointer = wlr_pointer_from_input_device(device);
|
||||||
struct cg_pointer *pointer = calloc(1, sizeof(struct cg_pointer));
|
struct cg_pointer *pointer = calloc(1, sizeof(struct cg_pointer));
|
||||||
if(!pointer) {
|
if(!pointer) {
|
||||||
wlr_log(WLR_ERROR, "Cannot allocate pointer");
|
wlr_log(WLR_ERROR, "Cannot allocate pointer");
|
||||||
|
|
@ -199,10 +130,11 @@ new_pointer(struct cg_seat *seat, struct cg_input_device *input_device) {
|
||||||
input_device->pointer = pointer;
|
input_device->pointer = pointer;
|
||||||
++seat->num_pointers;
|
++seat->num_pointers;
|
||||||
|
|
||||||
if(device->output_name != NULL) {
|
if(wlr_pointer->output_name != NULL) {
|
||||||
struct cg_output *output;
|
struct cg_output *output;
|
||||||
wl_list_for_each(output, &seat->server->outputs, link) {
|
wl_list_for_each(output, &seat->server->outputs, link) {
|
||||||
if(strcmp(device->output_name, output->wlr_output->name) == 0) {
|
if(strcmp(wlr_pointer->output_name, output->wlr_output->name) ==
|
||||||
|
0) {
|
||||||
wlr_cursor_map_input_to_output(seat->cursor, device,
|
wlr_cursor_map_input_to_output(seat->cursor, device,
|
||||||
output->wlr_output);
|
output->wlr_output);
|
||||||
break;
|
break;
|
||||||
|
|
@ -214,8 +146,7 @@ new_pointer(struct cg_seat *seat, struct cg_input_device *input_device) {
|
||||||
static int
|
static int
|
||||||
handle_keyboard_repeat(void *data) {
|
handle_keyboard_repeat(void *data) {
|
||||||
struct cg_keyboard_group *cg_group = data;
|
struct cg_keyboard_group *cg_group = data;
|
||||||
struct wlr_keyboard *wlr_device =
|
struct wlr_keyboard *wlr_device = &cg_group->wlr_group->keyboard;
|
||||||
cg_group->wlr_group->input_device->keyboard;
|
|
||||||
if(cg_group->repeat_keybinding != NULL) {
|
if(cg_group->repeat_keybinding != NULL) {
|
||||||
if(wlr_device->repeat_info.rate > 0) {
|
if(wlr_device->repeat_info.rate > 0) {
|
||||||
if(wl_event_source_timer_update(
|
if(wl_event_source_timer_update(
|
||||||
|
|
@ -234,9 +165,9 @@ handle_keyboard_repeat(void *data) {
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_modifier_event(struct wlr_input_device *device, struct cg_seat *seat) {
|
handle_modifier_event(struct wlr_input_device *device, struct cg_seat *seat) {
|
||||||
wlr_seat_set_keyboard(seat->seat, device);
|
struct wlr_keyboard *keyboard = wlr_keyboard_from_input_device(device);
|
||||||
wlr_seat_keyboard_notify_modifiers(seat->seat,
|
wlr_seat_set_keyboard(seat->seat, keyboard);
|
||||||
&device->keyboard->modifiers);
|
wlr_seat_keyboard_notify_modifiers(seat->seat, &keyboard->modifiers);
|
||||||
|
|
||||||
wlr_idle_notify_activity(seat->server->idle, seat->seat);
|
wlr_idle_notify_activity(seat->server->idle, seat->seat);
|
||||||
}
|
}
|
||||||
|
|
@ -256,24 +187,50 @@ static bool
|
||||||
handle_command_key_bindings(struct cg_server *server, xkb_keysym_t sym,
|
handle_command_key_bindings(struct cg_server *server, xkb_keysym_t sym,
|
||||||
uint32_t modifiers, uint32_t mode,
|
uint32_t modifiers, uint32_t mode,
|
||||||
struct cg_keyboard_group *group) {
|
struct cg_keyboard_group *group) {
|
||||||
|
if(group->enable_keybindings == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
struct keybinding **keybinding = find_keybinding(
|
struct keybinding **keybinding = find_keybinding(
|
||||||
server->keybindings,
|
server->keybindings,
|
||||||
&(struct keybinding){.key = sym, .mode = mode, .modifiers = modifiers});
|
&(struct keybinding){.key = sym, .mode = mode, .modifiers = modifiers});
|
||||||
server->seat->mode =
|
if(server->seat->mode != server->seat->default_mode) {
|
||||||
server->seat
|
server->seat->mode =
|
||||||
->default_mode; // Return to mode we are currently in by default
|
server->seat
|
||||||
|
->default_mode; // Return to mode we are currently in by default
|
||||||
|
double sx, sy;
|
||||||
|
struct wlr_seat *wlr_seat = server->seat->seat;
|
||||||
|
struct wlr_surface *surface = NULL;
|
||||||
|
|
||||||
|
struct wlr_scene_node *node = wlr_scene_node_at(
|
||||||
|
&server->scene->tree.node, server->seat->cursor->x,
|
||||||
|
server->seat->cursor->y, &sx, &sy);
|
||||||
|
if(server->seat->enable_cursor) {
|
||||||
|
wlr_xcursor_manager_set_cursor_image(server->seat->xcursor_manager,
|
||||||
|
"left_ptr",
|
||||||
|
server->seat->cursor);
|
||||||
|
if(node && node->type == WLR_SCENE_NODE_BUFFER) {
|
||||||
|
struct wlr_scene_surface *scene_surface =
|
||||||
|
wlr_scene_surface_from_buffer(
|
||||||
|
wlr_scene_buffer_from_node(node));
|
||||||
|
if(scene_surface != NULL) {
|
||||||
|
surface = scene_surface->surface;
|
||||||
|
}
|
||||||
|
wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(keybinding) {
|
if(keybinding) {
|
||||||
wlr_log(
|
wlr_log(
|
||||||
WLR_DEBUG,
|
WLR_DEBUG,
|
||||||
"Recognized keybinding pressed (key: %d, mode: %d, modifiers: %d)",
|
"Recognized keybinding pressed (key: %d, mode: %d, modifiers: %d)",
|
||||||
sym, mode, modifiers);
|
sym, mode, modifiers);
|
||||||
|
|
||||||
if(group->wlr_group->input_device->keyboard->repeat_info.delay > 0) {
|
if(group->wlr_group->keyboard.repeat_info.delay > 0) {
|
||||||
group->repeat_keybinding = keybinding;
|
group->repeat_keybinding = keybinding;
|
||||||
if(wl_event_source_timer_update(group->key_repeat_timer,
|
if(wl_event_source_timer_update(
|
||||||
group->wlr_group->input_device
|
group->key_repeat_timer,
|
||||||
->keyboard->repeat_info.delay) <
|
group->wlr_group->keyboard.repeat_info.delay) < 0) {
|
||||||
0) {
|
|
||||||
wlr_log(WLR_DEBUG, "failed to set key repeat timer");
|
wlr_log(WLR_DEBUG, "failed to set key repeat timer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -315,27 +272,25 @@ key_is_modifier(const xkb_keysym_t key) {
|
||||||
static void
|
static void
|
||||||
handle_key_event(struct cg_keyboard_group *group, struct cg_seat *seat,
|
handle_key_event(struct cg_keyboard_group *group, struct cg_seat *seat,
|
||||||
void *data) {
|
void *data) {
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_keyboard_key_event *event = data;
|
||||||
struct wlr_input_device *device = group->wlr_group->input_device;
|
struct wlr_keyboard *keyboard = &group->wlr_group->keyboard;
|
||||||
|
|
||||||
/* Translate from libinput keycode to an xkbcommon keycode. */
|
/* Translate from libinput keycode to an xkbcommon keycode. */
|
||||||
xkb_keycode_t keycode = event->keycode + 8;
|
xkb_keycode_t keycode = event->keycode + 8;
|
||||||
|
|
||||||
const xkb_keysym_t *syms;
|
const xkb_keysym_t *syms;
|
||||||
int nsyms =
|
int nsyms = xkb_state_key_get_syms(keyboard->xkb_state, keycode, &syms);
|
||||||
xkb_state_key_get_syms(device->keyboard->xkb_state, keycode, &syms);
|
|
||||||
|
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
|
|
||||||
for(int i = 0; i < nsyms; ++i) {
|
for(int i = 0; i < nsyms; ++i) {
|
||||||
if(event->state == WL_KEYBOARD_KEY_STATE_PRESSED &&
|
if(event->state == WL_KEYBOARD_KEY_STATE_PRESSED &&
|
||||||
!key_is_modifier(syms[i])) {
|
!key_is_modifier(syms[i])) {
|
||||||
uint32_t modifiers = wlr_keyboard_get_modifiers(device->keyboard);
|
uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
|
||||||
/* Get the consumed_modifiers and remove them from the modifier list
|
/* Get the consumed_modifiers and remove them from the modifier list
|
||||||
*/
|
*/
|
||||||
xkb_mod_mask_t consumed_modifiers =
|
xkb_mod_mask_t consumed_modifiers =
|
||||||
xkb_state_key_get_consumed_mods2(device->keyboard->xkb_state,
|
xkb_state_key_get_consumed_mods2(keyboard->xkb_state, keycode,
|
||||||
keycode,
|
|
||||||
XKB_CONSUMED_MODE_GTK);
|
XKB_CONSUMED_MODE_GTK);
|
||||||
if(handle_command_key_bindings(seat->server, syms[i],
|
if(handle_command_key_bindings(seat->server, syms[i],
|
||||||
modifiers & ~consumed_modifiers,
|
modifiers & ~consumed_modifiers,
|
||||||
|
|
@ -349,7 +304,7 @@ handle_key_event(struct cg_keyboard_group *group, struct cg_seat *seat,
|
||||||
|
|
||||||
if(!handled) {
|
if(!handled) {
|
||||||
/* Otherwise, we pass it along to the client. */
|
/* Otherwise, we pass it along to the client. */
|
||||||
wlr_seat_set_keyboard(seat->seat, device);
|
wlr_seat_set_keyboard(seat->seat, keyboard);
|
||||||
wlr_seat_keyboard_notify_key(seat->seat, event->time_msec,
|
wlr_seat_keyboard_notify_key(seat->seat, event->time_msec,
|
||||||
event->keycode, event->state);
|
event->keycode, event->state);
|
||||||
}
|
}
|
||||||
|
|
@ -368,18 +323,32 @@ static void
|
||||||
handle_keyboard_group_modifiers(struct wl_listener *listener, void *_data) {
|
handle_keyboard_group_modifiers(struct wl_listener *listener, void *_data) {
|
||||||
struct cg_keyboard_group *group =
|
struct cg_keyboard_group *group =
|
||||||
wl_container_of(listener, group, modifiers);
|
wl_container_of(listener, group, modifiers);
|
||||||
handle_modifier_event(group->wlr_group->input_device, group->seat);
|
handle_modifier_event(&group->wlr_group->keyboard.base, group->seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
repeat_info_match(struct wlr_keyboard *a, struct wlr_keyboard *b) {
|
||||||
|
return a->repeat_info.rate == b->repeat_info.rate &&
|
||||||
|
a->repeat_info.delay == b->repeat_info.delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cg_keyboard_group_add(struct wlr_input_device *device, struct cg_seat *seat) {
|
cg_keyboard_group_add(struct cg_input_device *input_device,
|
||||||
struct wlr_keyboard *wlr_keyboard = device->keyboard;
|
struct cg_seat *seat) {
|
||||||
struct cg_keyboard_group *group;
|
struct wlr_input_device *device = input_device->wlr_device;
|
||||||
wl_list_for_each(group, &seat->keyboard_groups, link) {
|
struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device);
|
||||||
struct wlr_keyboard_group *wlr_group = group->wlr_group;
|
// Virtual devices should not be grouped
|
||||||
if(wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard)) {
|
if(!input_device->is_virtual) {
|
||||||
wlr_log(WLR_DEBUG, "Adding keyboard to existing group.");
|
struct cg_keyboard_group *group;
|
||||||
return;
|
wl_list_for_each(group, &seat->keyboard_groups, link) {
|
||||||
|
struct wlr_keyboard_group *wlr_group = group->wlr_group;
|
||||||
|
if(wlr_keyboard_keymaps_match(wlr_keyboard->keymap,
|
||||||
|
wlr_group->keyboard.keymap) &&
|
||||||
|
repeat_info_match(wlr_keyboard, &wlr_group->keyboard) &&
|
||||||
|
wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard)) {
|
||||||
|
wlr_log(WLR_DEBUG, "Adding keyboard to existing group.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* This is reached if and only if the keyboard could not be inserted into
|
/* This is reached if and only if the keyboard could not be inserted into
|
||||||
|
|
@ -399,10 +368,15 @@ cg_keyboard_group_add(struct wlr_input_device *device, struct cg_seat *seat) {
|
||||||
|
|
||||||
cg_group->wlr_group->data = cg_group;
|
cg_group->wlr_group->data = cg_group;
|
||||||
wlr_keyboard_set_keymap(&cg_group->wlr_group->keyboard,
|
wlr_keyboard_set_keymap(&cg_group->wlr_group->keyboard,
|
||||||
device->keyboard->keymap);
|
wlr_keyboard->keymap);
|
||||||
wlr_keyboard_set_repeat_info(&cg_group->wlr_group->keyboard,
|
wlr_keyboard_set_repeat_info(&cg_group->wlr_group->keyboard,
|
||||||
device->keyboard->repeat_info.rate,
|
wlr_keyboard->repeat_info.rate,
|
||||||
device->keyboard->repeat_info.delay);
|
wlr_keyboard->repeat_info.delay);
|
||||||
|
if(input_device->identifier != NULL) {
|
||||||
|
cg_group->identifier = strdup(input_device->identifier);
|
||||||
|
} else {
|
||||||
|
cg_group->identifier = NULL;
|
||||||
|
}
|
||||||
wlr_log(WLR_DEBUG, "Created keyboard group.");
|
wlr_log(WLR_DEBUG, "Created keyboard group.");
|
||||||
|
|
||||||
wlr_keyboard_group_add_keyboard(cg_group->wlr_group, wlr_keyboard);
|
wlr_keyboard_group_add_keyboard(cg_group->wlr_group, wlr_keyboard);
|
||||||
|
|
@ -418,12 +392,17 @@ cg_keyboard_group_add(struct wlr_input_device *device, struct cg_seat *seat) {
|
||||||
seat->server->event_loop, handle_keyboard_repeat, cg_group);
|
seat->server->event_loop, handle_keyboard_repeat, cg_group);
|
||||||
|
|
||||||
cg_group->modifiers.notify = handle_keyboard_group_modifiers;
|
cg_group->modifiers.notify = handle_keyboard_group_modifiers;
|
||||||
|
cg_group->enable_keybindings = true;
|
||||||
|
cg_input_manager_configure_keyboard_group(cg_group);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if(cg_group && cg_group->wlr_group) {
|
if(cg_group && cg_group->wlr_group) {
|
||||||
wlr_keyboard_group_destroy(cg_group->wlr_group);
|
wlr_keyboard_group_destroy(cg_group->wlr_group);
|
||||||
}
|
}
|
||||||
|
if(cg_group && cg_group->identifier) {
|
||||||
|
free(cg_group->identifier);
|
||||||
|
}
|
||||||
free(cg_group);
|
free(cg_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,16 +424,15 @@ new_keyboard(struct cg_seat *seat, struct cg_input_device *input_device) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_keyboard_set_keymap(device->keyboard, keymap);
|
wlr_keyboard_set_keymap(wlr_keyboard_from_input_device(device), keymap);
|
||||||
|
|
||||||
xkb_keymap_unref(keymap);
|
xkb_keymap_unref(keymap);
|
||||||
xkb_context_unref(context);
|
xkb_context_unref(context);
|
||||||
wlr_keyboard_set_repeat_info(device->keyboard, 25, 600);
|
|
||||||
|
|
||||||
cg_keyboard_group_add(device, seat);
|
cg_keyboard_group_add(input_device, seat);
|
||||||
++seat->num_keyboards;
|
++seat->num_keyboards;
|
||||||
|
|
||||||
wlr_seat_set_keyboard(seat->seat, device);
|
wlr_seat_set_keyboard(seat->seat, wlr_keyboard_from_input_device(device));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -481,19 +459,46 @@ seat_add_device(struct cg_seat *seat, struct cg_input_device *device) {
|
||||||
update_capabilities(seat);
|
update_capabilities(seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
destroy_empty_wlr_keyboard_group(void *data) {
|
||||||
|
wlr_keyboard_group_destroy(data);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
remove_keyboard(struct cg_seat *seat, struct cg_input_device *keyboard) {
|
remove_keyboard(struct cg_seat *seat, struct cg_input_device *keyboard) {
|
||||||
if(!keyboard) {
|
if(!keyboard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct wlr_seat *wlr_seat = seat->seat;
|
struct wlr_seat *wlr_seat = seat->seat;
|
||||||
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_device->keyboard;
|
struct wlr_keyboard *wlr_keyboard =
|
||||||
if(keyboard->wlr_device->keyboard->group) {
|
wlr_keyboard_from_input_device(keyboard->wlr_device);
|
||||||
wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard);
|
struct wlr_keyboard_group *wlr_group = wlr_keyboard->group;
|
||||||
}
|
if(wlr_group) {
|
||||||
if(wlr_seat_get_keyboard(wlr_seat) == wlr_keyboard) {
|
wlr_keyboard_group_remove_keyboard(wlr_group, wlr_keyboard);
|
||||||
wlr_seat_set_keyboard(wlr_seat, NULL);
|
|
||||||
|
if(wl_list_empty(&wlr_group->devices)) {
|
||||||
|
wlr_log(WLR_DEBUG, "Destroying empty keyboard group %p",
|
||||||
|
(void *)wlr_group);
|
||||||
|
struct cg_keyboard_group *group = wlr_group->data;
|
||||||
|
if(wlr_seat_get_keyboard(wlr_seat) == &wlr_group->keyboard) {
|
||||||
|
wlr_seat_set_keyboard(wlr_seat, NULL);
|
||||||
|
}
|
||||||
|
wlr_group->data = NULL;
|
||||||
|
wl_list_remove(&group->link);
|
||||||
|
wl_list_remove(&group->key.link);
|
||||||
|
wl_list_remove(&group->modifiers.link);
|
||||||
|
if(group->identifier != NULL) {
|
||||||
|
free(group->identifier);
|
||||||
|
}
|
||||||
|
free(group);
|
||||||
|
|
||||||
|
// To prevent use-after-free conditions when handling key events,
|
||||||
|
// defer freeing the wlr_keyboard_group until idle
|
||||||
|
wl_event_loop_add_idle(seat->server->event_loop,
|
||||||
|
destroy_empty_wlr_keyboard_group, wlr_group);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
--seat->num_keyboards;
|
--seat->num_keyboards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -544,6 +549,9 @@ handle_request_set_selection(struct wl_listener *listener, void *data) {
|
||||||
static void
|
static void
|
||||||
handle_request_set_cursor(struct wl_listener *listener, void *data) {
|
handle_request_set_cursor(struct wl_listener *listener, void *data) {
|
||||||
struct cg_seat *seat = wl_container_of(listener, seat, request_set_cursor);
|
struct cg_seat *seat = wl_container_of(listener, seat, request_set_cursor);
|
||||||
|
if(seat->enable_cursor == false || seat->mode != seat->default_mode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
struct wlr_seat_pointer_request_set_cursor_event *event = data;
|
struct wlr_seat_pointer_request_set_cursor_event *event = data;
|
||||||
struct wlr_surface *focused_surface =
|
struct wlr_surface *focused_surface =
|
||||||
event->seat_client->seat->pointer_state.focused_surface;
|
event->seat_client->seat->pointer_state.focused_surface;
|
||||||
|
|
@ -565,21 +573,25 @@ handle_request_set_cursor(struct wl_listener *listener, void *data) {
|
||||||
static void
|
static void
|
||||||
handle_touch_down(struct wl_listener *listener, void *data) {
|
handle_touch_down(struct wl_listener *listener, void *data) {
|
||||||
struct cg_seat *seat = wl_container_of(listener, seat, touch_down);
|
struct cg_seat *seat = wl_container_of(listener, seat, touch_down);
|
||||||
struct wlr_event_touch_down *event = data;
|
struct wlr_touch_down_event *event = data;
|
||||||
|
|
||||||
double lx, ly;
|
double lx, ly;
|
||||||
wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device, event->x,
|
wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->touch->base,
|
||||||
event->y, &lx, &ly);
|
event->x, event->y, &lx, &ly);
|
||||||
|
|
||||||
double sx, sy;
|
double sx, sy;
|
||||||
struct wlr_surface *surface;
|
struct wlr_scene_node *node =
|
||||||
struct cg_view *view =
|
wlr_scene_node_at(&seat->server->scene->tree.node, lx, ly, &sx, &sy);
|
||||||
desktop_view_at(seat->server, lx, ly, &surface, &sx, &sy);
|
|
||||||
|
|
||||||
uint32_t serial = 0;
|
uint32_t serial = 0;
|
||||||
if(view) {
|
if(node && node->type == WLR_SCENE_NODE_BUFFER) {
|
||||||
serial = wlr_seat_touch_notify_down(
|
struct wlr_scene_surface *scene_surface =
|
||||||
seat->seat, surface, event->time_msec, event->touch_id, sx, sy);
|
wlr_scene_surface_from_buffer(wlr_scene_buffer_from_node(node));
|
||||||
|
if(scene_surface != NULL) {
|
||||||
|
serial = wlr_seat_touch_notify_down(
|
||||||
|
seat->seat, scene_surface->surface, event->time_msec,
|
||||||
|
event->touch_id, sx, sy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(serial && wlr_seat_touch_num_points(seat->seat) == 1) {
|
if(serial && wlr_seat_touch_num_points(seat->seat) == 1) {
|
||||||
|
|
@ -594,7 +606,7 @@ handle_touch_down(struct wl_listener *listener, void *data) {
|
||||||
static void
|
static void
|
||||||
handle_touch_up(struct wl_listener *listener, void *data) {
|
handle_touch_up(struct wl_listener *listener, void *data) {
|
||||||
struct cg_seat *seat = wl_container_of(listener, seat, touch_up);
|
struct cg_seat *seat = wl_container_of(listener, seat, touch_up);
|
||||||
struct wlr_event_touch_up *event = data;
|
struct wlr_touch_up_event *event = data;
|
||||||
|
|
||||||
if(!wlr_seat_touch_get_point(seat->seat, event->touch_id)) {
|
if(!wlr_seat_touch_get_point(seat->seat, event->touch_id)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -607,24 +619,29 @@ handle_touch_up(struct wl_listener *listener, void *data) {
|
||||||
static void
|
static void
|
||||||
handle_touch_motion(struct wl_listener *listener, void *data) {
|
handle_touch_motion(struct wl_listener *listener, void *data) {
|
||||||
struct cg_seat *seat = wl_container_of(listener, seat, touch_motion);
|
struct cg_seat *seat = wl_container_of(listener, seat, touch_motion);
|
||||||
struct wlr_event_touch_motion *event = data;
|
struct wlr_touch_motion_event *event = data;
|
||||||
|
|
||||||
if(!wlr_seat_touch_get_point(seat->seat, event->touch_id)) {
|
if(!wlr_seat_touch_get_point(seat->seat, event->touch_id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double lx, ly;
|
double lx, ly;
|
||||||
wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device, event->x,
|
wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->touch->base,
|
||||||
event->y, &lx, &ly);
|
event->x, event->y, &lx, &ly);
|
||||||
|
|
||||||
double sx, sy;
|
double sx, sy;
|
||||||
struct wlr_surface *surface;
|
struct wlr_scene_node *node =
|
||||||
struct cg_view *view =
|
wlr_scene_node_at(&seat->server->scene->tree.node, lx, ly, &sx, &sy);
|
||||||
desktop_view_at(seat->server, lx, ly, &surface, &sx, &sy);
|
|
||||||
|
|
||||||
if(view) {
|
if(node && node->type == WLR_SCENE_NODE_BUFFER) {
|
||||||
wlr_seat_touch_point_focus(seat->seat, surface, event->time_msec,
|
struct wlr_scene_surface *scene_surface =
|
||||||
event->touch_id, sx, sy);
|
wlr_scene_surface_from_buffer(wlr_scene_buffer_from_node(node));
|
||||||
|
if(scene_surface != NULL) {
|
||||||
|
|
||||||
|
wlr_seat_touch_point_focus(seat->seat, scene_surface->surface,
|
||||||
|
event->time_msec, event->touch_id, sx,
|
||||||
|
sy);
|
||||||
|
}
|
||||||
wlr_seat_touch_notify_motion(seat->seat, event->time_msec,
|
wlr_seat_touch_notify_motion(seat->seat, event->time_msec,
|
||||||
event->touch_id, sx, sy);
|
event->touch_id, sx, sy);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -651,7 +668,7 @@ handle_cursor_frame(struct wl_listener *listener, void *_data) {
|
||||||
static void
|
static void
|
||||||
handle_cursor_axis(struct wl_listener *listener, void *data) {
|
handle_cursor_axis(struct wl_listener *listener, void *data) {
|
||||||
struct cg_seat *seat = wl_container_of(listener, seat, cursor_axis);
|
struct cg_seat *seat = wl_container_of(listener, seat, cursor_axis);
|
||||||
struct wlr_event_pointer_axis *event = data;
|
struct wlr_pointer_axis_event *event = data;
|
||||||
|
|
||||||
wlr_seat_pointer_notify_axis(seat->seat, event->time_msec,
|
wlr_seat_pointer_notify_axis(seat->seat, event->time_msec,
|
||||||
event->orientation, event->delta,
|
event->orientation, event->delta,
|
||||||
|
|
@ -662,7 +679,7 @@ handle_cursor_axis(struct wl_listener *listener, void *data) {
|
||||||
static void
|
static void
|
||||||
handle_cursor_button(struct wl_listener *listener, void *data) {
|
handle_cursor_button(struct wl_listener *listener, void *data) {
|
||||||
struct cg_seat *seat = wl_container_of(listener, seat, cursor_button);
|
struct cg_seat *seat = wl_container_of(listener, seat, cursor_button);
|
||||||
struct wlr_event_pointer_button *event = data;
|
struct wlr_pointer_button_event *event = data;
|
||||||
|
|
||||||
wlr_seat_pointer_notify_button(seat->seat, event->time_msec, event->button,
|
wlr_seat_pointer_notify_button(seat->seat, event->time_msec, event->button,
|
||||||
event->state);
|
event->state);
|
||||||
|
|
@ -675,18 +692,24 @@ process_cursor_motion(struct cg_seat *seat, uint32_t time) {
|
||||||
struct wlr_seat *wlr_seat = seat->seat;
|
struct wlr_seat *wlr_seat = seat->seat;
|
||||||
struct wlr_surface *surface = NULL;
|
struct wlr_surface *surface = NULL;
|
||||||
|
|
||||||
struct cg_view *view = desktop_view_at(seat->server, seat->cursor->x,
|
struct wlr_scene_node *node =
|
||||||
seat->cursor->y, &surface, &sx, &sy);
|
wlr_scene_node_at(&seat->server->scene->tree.node, seat->cursor->x,
|
||||||
|
seat->cursor->y, &sx, &sy);
|
||||||
|
|
||||||
if(!view) {
|
if(node && node->type == WLR_SCENE_NODE_BUFFER) {
|
||||||
wlr_seat_pointer_clear_focus(wlr_seat);
|
struct wlr_scene_surface *scene_surface =
|
||||||
} else {
|
wlr_scene_surface_from_buffer(wlr_scene_buffer_from_node(node));
|
||||||
|
if(scene_surface != NULL) {
|
||||||
|
surface = scene_surface->surface;
|
||||||
|
}
|
||||||
wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
|
wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
|
||||||
|
|
||||||
bool focus_changed = wlr_seat->pointer_state.focused_surface != surface;
|
bool focus_changed = wlr_seat->pointer_state.focused_surface != surface;
|
||||||
if(!focus_changed && time > 0) {
|
if(!focus_changed && time > 0) {
|
||||||
wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
|
wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
wlr_seat_pointer_clear_focus(wlr_seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cg_drag_icon *drag_icon;
|
struct cg_drag_icon *drag_icon;
|
||||||
|
|
@ -695,15 +718,54 @@ process_cursor_motion(struct cg_seat *seat, uint32_t time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_idle_notify_activity(seat->server->idle, seat->seat);
|
wlr_idle_notify_activity(seat->server->idle, seat->seat);
|
||||||
|
|
||||||
|
/* Check if cursor switched tile */
|
||||||
|
struct wlr_output *c_outp = wlr_output_layout_output_at(
|
||||||
|
seat->server->output_layout, seat->cursor->x, seat->cursor->y);
|
||||||
|
struct cg_output *cg_outp = NULL;
|
||||||
|
wl_list_for_each(cg_outp, &seat->server->outputs, link) {
|
||||||
|
if(cg_outp->wlr_output == c_outp) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct cg_tile *c_tile;
|
||||||
|
bool first = true;
|
||||||
|
for(c_tile = cg_outp->workspaces[cg_outp->curr_workspace]->focused_tile;
|
||||||
|
first ||
|
||||||
|
c_tile != cg_outp->workspaces[cg_outp->curr_workspace]->focused_tile;
|
||||||
|
c_tile = c_tile->next) {
|
||||||
|
first = false;
|
||||||
|
double ox = seat->cursor->x, oy = seat->cursor->y;
|
||||||
|
wlr_output_layout_output_coords(seat->server->output_layout, c_outp,
|
||||||
|
&ox, &oy);
|
||||||
|
if(c_tile->tile.x <= ox && c_tile->tile.y <= oy &&
|
||||||
|
c_tile->tile.x + c_tile->tile.width >= ox &&
|
||||||
|
c_tile->tile.y + c_tile->tile.height >= oy) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(seat->cursor_tile != NULL && seat->cursor_tile != c_tile) {
|
||||||
|
ipc_send_event(seat->server,
|
||||||
|
"{\"event_name\":\"cursor_switch_tile\",\"old_output\":"
|
||||||
|
"\"%s\",\"old_output_id\":%d,"
|
||||||
|
"\"old_tile\":%d,\"new_output\":\"%s\",\"new_output_"
|
||||||
|
"id\":%d,\"new_tile\":%d}",
|
||||||
|
seat->cursor_tile->workspace->output->wlr_output->name,
|
||||||
|
output_get_num(seat->cursor_tile->workspace->output),
|
||||||
|
seat->cursor_tile->id, c_outp->name,
|
||||||
|
output_get_num(cg_outp), c_tile->id);
|
||||||
|
}
|
||||||
|
seat->cursor_tile = c_tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_cursor_motion_absolute(struct wl_listener *listener, void *data) {
|
handle_cursor_motion_absolute(struct wl_listener *listener, void *data) {
|
||||||
struct cg_seat *seat =
|
struct cg_seat *seat =
|
||||||
wl_container_of(listener, seat, cursor_motion_absolute);
|
wl_container_of(listener, seat, cursor_motion_absolute);
|
||||||
struct wlr_event_pointer_motion_absolute *event = data;
|
struct wlr_pointer_motion_absolute_event *event = data;
|
||||||
|
|
||||||
wlr_cursor_warp_absolute(seat->cursor, event->device, event->x, event->y);
|
wlr_cursor_warp_absolute(seat->cursor, &event->pointer->base, event->x,
|
||||||
|
event->y);
|
||||||
process_cursor_motion(seat, event->time_msec);
|
process_cursor_motion(seat, event->time_msec);
|
||||||
wlr_idle_notify_activity(seat->server->idle, seat->seat);
|
wlr_idle_notify_activity(seat->server->idle, seat->seat);
|
||||||
}
|
}
|
||||||
|
|
@ -711,34 +773,20 @@ handle_cursor_motion_absolute(struct wl_listener *listener, void *data) {
|
||||||
static void
|
static void
|
||||||
handle_cursor_motion(struct wl_listener *listener, void *data) {
|
handle_cursor_motion(struct wl_listener *listener, void *data) {
|
||||||
struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion);
|
struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion);
|
||||||
struct wlr_event_pointer_motion *event = data;
|
struct wlr_pointer_motion_event *event = data;
|
||||||
|
|
||||||
wlr_cursor_move(seat->cursor, event->device, event->delta_x,
|
wlr_cursor_move(seat->cursor, &event->pointer->base, event->delta_x,
|
||||||
event->delta_y);
|
event->delta_y);
|
||||||
process_cursor_motion(seat, event->time_msec);
|
process_cursor_motion(seat, event->time_msec);
|
||||||
wlr_idle_notify_activity(seat->server->idle, seat->seat);
|
wlr_idle_notify_activity(seat->server->idle, seat->seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
drag_icon_damage(struct cg_drag_icon *drag_icon) {
|
|
||||||
struct cg_output *output;
|
|
||||||
wl_list_for_each(output, &drag_icon->seat->server->outputs, link) {
|
|
||||||
struct wlr_box *output_layout_box = wlr_output_layout_get_box(
|
|
||||||
output->server->output_layout, output->wlr_output);
|
|
||||||
output_damage_surface(output, drag_icon->wlr_drag_icon->surface,
|
|
||||||
drag_icon->lx - output_layout_box->x,
|
|
||||||
drag_icon->ly - output_layout_box->y, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
drag_icon_update_position(struct cg_drag_icon *drag_icon) {
|
drag_icon_update_position(struct cg_drag_icon *drag_icon) {
|
||||||
struct wlr_drag_icon *wlr_icon = drag_icon->wlr_drag_icon;
|
struct wlr_drag_icon *wlr_icon = drag_icon->wlr_drag_icon;
|
||||||
struct cg_seat *seat = drag_icon->seat;
|
struct cg_seat *seat = drag_icon->seat;
|
||||||
struct wlr_touch_point *point;
|
struct wlr_touch_point *point;
|
||||||
|
|
||||||
drag_icon_damage(drag_icon);
|
|
||||||
|
|
||||||
switch(wlr_icon->drag->grab_type) {
|
switch(wlr_icon->drag->grab_type) {
|
||||||
case WLR_DRAG_GRAB_KEYBOARD:
|
case WLR_DRAG_GRAB_KEYBOARD:
|
||||||
return;
|
return;
|
||||||
|
|
@ -755,8 +803,8 @@ drag_icon_update_position(struct cg_drag_icon *drag_icon) {
|
||||||
drag_icon->ly = seat->touch_ly;
|
drag_icon->ly = seat->touch_ly;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
wlr_scene_node_set_position(&drag_icon->scene_tree->node, drag_icon->lx,
|
||||||
drag_icon_damage(drag_icon);
|
drag_icon->ly);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -764,9 +812,9 @@ handle_drag_icon_destroy(struct wl_listener *listener, void *_data) {
|
||||||
struct cg_drag_icon *drag_icon =
|
struct cg_drag_icon *drag_icon =
|
||||||
wl_container_of(listener, drag_icon, destroy);
|
wl_container_of(listener, drag_icon, destroy);
|
||||||
|
|
||||||
drag_icon_damage(drag_icon);
|
|
||||||
wl_list_remove(&drag_icon->link);
|
wl_list_remove(&drag_icon->link);
|
||||||
wl_list_remove(&drag_icon->destroy.link);
|
wl_list_remove(&drag_icon->destroy.link);
|
||||||
|
wlr_scene_node_destroy(&drag_icon->scene_tree->node);
|
||||||
free(drag_icon);
|
free(drag_icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -789,7 +837,6 @@ handle_request_start_drag(struct wl_listener *listener, void *data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: tablet grabs
|
|
||||||
wlr_log(WLR_DEBUG,
|
wlr_log(WLR_DEBUG,
|
||||||
"Ignoring start_drag request: "
|
"Ignoring start_drag request: "
|
||||||
"could not validate pointer/touch serial %" PRIu32,
|
"could not validate pointer/touch serial %" PRIu32,
|
||||||
|
|
@ -812,6 +859,12 @@ handle_start_drag(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
drag_icon->seat = seat;
|
drag_icon->seat = seat;
|
||||||
drag_icon->wlr_drag_icon = wlr_drag_icon;
|
drag_icon->wlr_drag_icon = wlr_drag_icon;
|
||||||
|
drag_icon->scene_tree = wlr_scene_subsurface_tree_create(
|
||||||
|
&seat->server->scene->tree, wlr_drag_icon->surface);
|
||||||
|
if(!drag_icon->scene_tree) {
|
||||||
|
free(drag_icon);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
drag_icon->destroy.notify = handle_drag_icon_destroy;
|
drag_icon->destroy.notify = handle_drag_icon_destroy;
|
||||||
wl_signal_add(&wlr_drag_icon->events.destroy, &drag_icon->destroy);
|
wl_signal_add(&wlr_drag_icon->events.destroy, &drag_icon->destroy);
|
||||||
|
|
@ -831,6 +884,9 @@ handle_destroy(struct wl_listener *listener, void *_data) {
|
||||||
wl_list_remove(&group->link);
|
wl_list_remove(&group->link);
|
||||||
wlr_keyboard_group_destroy(group->wlr_group);
|
wlr_keyboard_group_destroy(group->wlr_group);
|
||||||
wl_event_source_remove(group->key_repeat_timer);
|
wl_event_source_remove(group->key_repeat_timer);
|
||||||
|
if(group->identifier) {
|
||||||
|
free(group->identifier);
|
||||||
|
}
|
||||||
free(group);
|
free(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -865,6 +921,7 @@ seat_create(struct cg_server *server, struct wlr_backend *backend) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seat->enable_cursor = true;
|
||||||
seat->seat = wlr_seat_create(server->wl_display, "seat0");
|
seat->seat = wlr_seat_create(server->wl_display, "seat0");
|
||||||
if(!seat->seat) {
|
if(!seat->seat) {
|
||||||
wlr_log(WLR_ERROR, "Cannot allocate seat0");
|
wlr_log(WLR_ERROR, "Cannot allocate seat0");
|
||||||
|
|
@ -885,7 +942,8 @@ seat_create(struct cg_server *server, struct wlr_backend *backend) {
|
||||||
wlr_cursor_attach_output_layout(seat->cursor, server->output_layout);
|
wlr_cursor_attach_output_layout(seat->cursor, server->output_layout);
|
||||||
|
|
||||||
if(!seat->xcursor_manager) {
|
if(!seat->xcursor_manager) {
|
||||||
seat->xcursor_manager = wlr_xcursor_manager_create(NULL, XCURSOR_SIZE);
|
seat->xcursor_manager =
|
||||||
|
wlr_xcursor_manager_create(NULL, server->xcursor_size);
|
||||||
if(!seat->xcursor_manager) {
|
if(!seat->xcursor_manager) {
|
||||||
wlr_log(WLR_ERROR, "Cannot create XCursor manager");
|
wlr_log(WLR_ERROR, "Cannot create XCursor manager");
|
||||||
wlr_cursor_destroy(seat->cursor);
|
wlr_cursor_destroy(seat->cursor);
|
||||||
|
|
@ -971,8 +1029,10 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view) {
|
||||||
|
|
||||||
/* Focusing the background */
|
/* Focusing the background */
|
||||||
if(view == NULL) {
|
if(view == NULL) {
|
||||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
workspace_tile_update_view(
|
||||||
->focused_tile->view = NULL;
|
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||||
|
->focused_tile,
|
||||||
|
NULL);
|
||||||
seat->focused_view = NULL;
|
seat->focused_view = NULL;
|
||||||
if(prev_view != NULL) {
|
if(prev_view != NULL) {
|
||||||
view_activate(prev_view, false);
|
view_activate(prev_view, false);
|
||||||
|
|
@ -985,24 +1045,18 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view) {
|
||||||
if(view->type != CG_XWAYLAND_VIEW || xwayland_view_should_manage(view))
|
if(view->type != CG_XWAYLAND_VIEW || xwayland_view_should_manage(view))
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
/* Always resize the view, even if prev_view == view */
|
|
||||||
struct cg_workspace *curr_workspace =
|
struct cg_workspace *curr_workspace =
|
||||||
server->curr_output
|
server->curr_output
|
||||||
->workspaces[server->curr_output->curr_workspace];
|
->workspaces[server->curr_output->curr_workspace];
|
||||||
view_maximize(view, curr_workspace->focused_tile);
|
workspace_tile_update_view(curr_workspace->focused_tile, view);
|
||||||
if(!view_is_visible(view)) {
|
wl_list_remove(&curr_workspace->views);
|
||||||
wl_list_remove(&view->link);
|
wl_list_insert(&view->link, &curr_workspace->views);
|
||||||
if(curr_workspace->focused_tile->view != NULL) {
|
|
||||||
wl_list_insert(&curr_workspace->focused_tile->view->link,
|
|
||||||
&view->link);
|
|
||||||
} else {
|
|
||||||
wl_list_insert(curr_workspace->views.prev, &view->link);
|
|
||||||
}
|
|
||||||
curr_workspace->focused_tile->view = view;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CG_HAS_XWAYLAND
|
#if CG_HAS_XWAYLAND
|
||||||
|
if(view->type == CG_XWAYLAND_VIEW) {
|
||||||
|
wlr_xwayland_set_seat(server->xwayland, seat->seat);
|
||||||
|
}
|
||||||
if(view->type == CG_XWAYLAND_VIEW && !xwayland_view_should_manage(view)) {
|
if(view->type == CG_XWAYLAND_VIEW && !xwayland_view_should_manage(view)) {
|
||||||
const struct cg_xwayland_view *xwayland_view =
|
const struct cg_xwayland_view *xwayland_view =
|
||||||
xwayland_view_from_view(view);
|
xwayland_view_from_view(view);
|
||||||
|
|
@ -1021,10 +1075,6 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
view_activate(view, true);
|
view_activate(view, true);
|
||||||
char *title = view_get_title(view);
|
|
||||||
|
|
||||||
output_set_window_title(server->curr_output, title);
|
|
||||||
free(title);
|
|
||||||
|
|
||||||
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(wlr_seat);
|
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(wlr_seat);
|
||||||
wlr_seat_keyboard_end_grab(wlr_seat);
|
wlr_seat_keyboard_end_grab(wlr_seat);
|
||||||
|
|
@ -1037,5 +1087,11 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view) {
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_scene_node_raise_to_top(&view->scene_tree->node);
|
||||||
process_cursor_motion(seat, -1);
|
process_cursor_motion(seat, -1);
|
||||||
|
struct wlr_box box;
|
||||||
|
wlr_output_layout_get_box(server->output_layout,
|
||||||
|
view->workspace->output->wlr_output, &box);
|
||||||
|
wlr_scene_node_set_position(&view->workspace->output->bg->node, box.x,
|
||||||
|
box.y);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
seat.h
11
seat.h
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef CG_SEAT_H
|
#ifndef CG_SEAT_H
|
||||||
#define CG_SEAT_H
|
#define CG_SEAT_H
|
||||||
|
|
||||||
|
|
@ -11,6 +14,8 @@ struct cg_input_device;
|
||||||
struct wlr_seat;
|
struct wlr_seat;
|
||||||
struct wlr_xcursor_manager;
|
struct wlr_xcursor_manager;
|
||||||
struct wlr_backend;
|
struct wlr_backend;
|
||||||
|
struct wlr_surface;
|
||||||
|
struct cg_input_config;
|
||||||
|
|
||||||
#define DEFAULT_XCURSOR "left_ptr"
|
#define DEFAULT_XCURSOR "left_ptr"
|
||||||
#define XCURSOR_SIZE 24
|
#define XCURSOR_SIZE 24
|
||||||
|
|
@ -26,7 +31,9 @@ struct cg_seat {
|
||||||
uint16_t num_pointers;
|
uint16_t num_pointers;
|
||||||
uint16_t num_touch;
|
uint16_t num_touch;
|
||||||
|
|
||||||
|
bool enable_cursor;
|
||||||
struct wlr_cursor *cursor;
|
struct wlr_cursor *cursor;
|
||||||
|
struct cg_tile *cursor_tile;
|
||||||
struct wlr_xcursor_manager *xcursor_manager;
|
struct wlr_xcursor_manager *xcursor_manager;
|
||||||
struct wl_listener cursor_motion;
|
struct wl_listener cursor_motion;
|
||||||
struct wl_listener cursor_motion_absolute;
|
struct wl_listener cursor_motion_absolute;
|
||||||
|
|
@ -60,6 +67,8 @@ struct cg_seat {
|
||||||
struct cg_keyboard_group {
|
struct cg_keyboard_group {
|
||||||
struct wlr_keyboard_group *wlr_group;
|
struct wlr_keyboard_group *wlr_group;
|
||||||
struct cg_seat *seat;
|
struct cg_seat *seat;
|
||||||
|
char *identifier;
|
||||||
|
int enable_keybindings;
|
||||||
|
|
||||||
struct wl_listener key;
|
struct wl_listener key;
|
||||||
struct wl_listener modifiers;
|
struct wl_listener modifiers;
|
||||||
|
|
@ -85,6 +94,7 @@ struct cg_drag_icon {
|
||||||
struct wl_list link; // seat::drag_icons
|
struct wl_list link; // seat::drag_icons
|
||||||
struct cg_seat *seat;
|
struct cg_seat *seat;
|
||||||
struct wlr_drag_icon *wlr_drag_icon;
|
struct wlr_drag_icon *wlr_drag_icon;
|
||||||
|
struct wlr_scene_tree *scene_tree;
|
||||||
|
|
||||||
/* The drag icon has a position in layout coordinates. */
|
/* The drag icon has a position in layout coordinates. */
|
||||||
double lx, ly;
|
double lx, ly;
|
||||||
|
|
@ -104,5 +114,4 @@ void
|
||||||
seat_add_device(struct cg_seat *seat, struct cg_input_device *device);
|
seat_add_device(struct cg_seat *seat, struct cg_input_device *device);
|
||||||
void
|
void
|
||||||
seat_remove_device(struct cg_seat *seat, struct cg_input_device *device);
|
seat_remove_device(struct cg_seat *seat, struct cg_input_device *device);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
4
server.c
4
server.c
|
|
@ -25,7 +25,6 @@ display_terminate(struct cg_server *server) {
|
||||||
if(server == NULL) {
|
if(server == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
server->running = false;
|
|
||||||
wl_display_terminate(server->wl_display);
|
wl_display_terminate(server->wl_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,14 +57,15 @@ server_show_info(struct cg_server *server) {
|
||||||
struct cg_input_device *input;
|
struct cg_input_device *input;
|
||||||
wl_list_for_each(input, &server->input->devices, link) {
|
wl_list_for_each(input, &server->input->devices, link) {
|
||||||
if(!input_str) {
|
if(!input_str) {
|
||||||
|
free(output_str);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
input_str_tmp = input_str;
|
input_str_tmp = input_str;
|
||||||
if(strcmp(input->identifier, "") != 0) {
|
if(strcmp(input->identifier, "") != 0) {
|
||||||
input_str =
|
input_str =
|
||||||
malloc_vsprintf("%s\t * %s\n", input_str, input->identifier);
|
malloc_vsprintf("%s\t * %s\n", input_str, input->identifier);
|
||||||
|
free(input_str_tmp);
|
||||||
}
|
}
|
||||||
free(input_str_tmp);
|
|
||||||
}
|
}
|
||||||
char *ret =
|
char *ret =
|
||||||
malloc_vsprintf("Outputs:\n%sInputs:\n%s", output_str, input_str);
|
malloc_vsprintf("Outputs:\n%sInputs:\n%s", output_str, input_str);
|
||||||
|
|
|
||||||
18
server.h
18
server.h
|
|
@ -23,8 +23,6 @@ struct cg_server {
|
||||||
struct cg_seat *seat;
|
struct cg_seat *seat;
|
||||||
struct cg_input_manager *input;
|
struct cg_input_manager *input;
|
||||||
struct wlr_backend *backend;
|
struct wlr_backend *backend;
|
||||||
struct wlr_renderer *renderer;
|
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
struct wlr_idle *idle;
|
struct wlr_idle *idle;
|
||||||
struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1;
|
struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1;
|
||||||
struct wl_listener new_idle_inhibitor_v1;
|
struct wl_listener new_idle_inhibitor_v1;
|
||||||
|
|
@ -35,11 +33,17 @@ struct cg_server {
|
||||||
struct wl_list outputs;
|
struct wl_list outputs;
|
||||||
struct cg_output *curr_output;
|
struct cg_output *curr_output;
|
||||||
struct wl_listener new_output;
|
struct wl_listener new_output;
|
||||||
|
struct wl_list output_priorities;
|
||||||
|
|
||||||
|
struct wlr_renderer *renderer;
|
||||||
|
struct wlr_allocator *allocator;
|
||||||
|
struct wlr_scene *scene;
|
||||||
|
|
||||||
struct wl_listener xdg_toplevel_decoration;
|
struct wl_listener xdg_toplevel_decoration;
|
||||||
struct wl_listener new_xdg_shell_surface;
|
struct wl_listener new_xdg_shell_surface;
|
||||||
#if CG_HAS_XWAYLAND
|
#if CG_HAS_XWAYLAND
|
||||||
struct wl_listener new_xwayland_surface;
|
struct wl_listener new_xwayland_surface;
|
||||||
|
struct wlr_xwayland *xwayland;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct keybinding_list *keybindings;
|
struct keybinding_list *keybindings;
|
||||||
|
|
@ -47,18 +51,16 @@ struct cg_server {
|
||||||
struct wl_list input_config;
|
struct wl_list input_config;
|
||||||
struct cg_message_config message_config;
|
struct cg_message_config message_config;
|
||||||
|
|
||||||
enum wl_output_transform output_transform;
|
|
||||||
|
|
||||||
struct cg_ipc_handle ipc;
|
struct cg_ipc_handle ipc;
|
||||||
|
|
||||||
|
bool enable_socket;
|
||||||
bool running;
|
bool running;
|
||||||
char **modes;
|
char **modes;
|
||||||
uint16_t nws;
|
uint16_t nws;
|
||||||
uint16_t message_timeout;
|
|
||||||
float *bg_color;
|
float *bg_color;
|
||||||
#ifdef DEBUG
|
uint32_t views_curr_id;
|
||||||
bool debug_damage_tracking;
|
uint32_t tiles_curr_id;
|
||||||
#endif
|
uint32_t xcursor_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
BIN
signatures/1.9.1-cagebreak-config.sig
Normal file
BIN
signatures/1.9.1-cagebreak-config.sig
Normal file
Binary file not shown.
BIN
signatures/1.9.1-cagebreak.1.sig
Normal file
BIN
signatures/1.9.1-cagebreak.1.sig
Normal file
Binary file not shown.
BIN
signatures/1.9.1-cagebreak.sig
Normal file
BIN
signatures/1.9.1-cagebreak.sig
Normal file
Binary file not shown.
Binary file not shown.
BIN
signatures/cagebreak-socket.7.sig
Normal file
BIN
signatures/cagebreak-socket.7.sig
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
11
util.c
11
util.c
|
|
@ -1,15 +1,10 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2022 The Cagebreak Authors
|
|
||||||
* Copyright (C) 2019 The Sway authors
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <wlr/util/box.h>
|
#include <wlr/util/box.h>
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
||||||
3
util.h
3
util.h
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef CG_UTIL_H
|
#ifndef CG_UTIL_H
|
||||||
#define CG_UTIL_H
|
#define CG_UTIL_H
|
||||||
|
|
||||||
|
|
|
||||||
265
view.c
265
view.c
|
|
@ -1,11 +1,5 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2022 The Cagebreak Authors
|
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
|
@ -15,9 +9,11 @@
|
||||||
#include <wlr/types/wlr_compositor.h>
|
#include <wlr/types/wlr_compositor.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
#include <wlr/types/wlr_output_damage.h>
|
#include <wlr/types/wlr_output_damage.h>
|
||||||
#include <wlr/types/wlr_surface.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
|
#include <wlr/types/wlr_scene.h>
|
||||||
#include <wlr/util/box.h>
|
#include <wlr/util/box.h>
|
||||||
|
|
||||||
|
#include "ipc_server.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "seat.h"
|
#include "seat.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
@ -49,127 +45,6 @@ view_get_prev_view(struct cg_view *view) {
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
view_damage_child(struct cg_view_child *child, bool whole) {
|
|
||||||
if(child->view != NULL) {
|
|
||||||
view_damage_part(child->view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
view_child_handle_commit(struct wl_listener *listener, void *_data) {
|
|
||||||
struct cg_view_child *child = wl_container_of(listener, child, commit);
|
|
||||||
view_damage_child(child, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
subsurface_create(struct cg_view_child *parent, struct cg_view *view,
|
|
||||||
struct wlr_subsurface *wlr_subsurface);
|
|
||||||
|
|
||||||
static void
|
|
||||||
view_child_handle_new_subsurface(struct wl_listener *listener, void *data) {
|
|
||||||
struct cg_view_child *child =
|
|
||||||
wl_container_of(listener, child, new_subsurface);
|
|
||||||
struct wlr_subsurface *wlr_subsurface = data;
|
|
||||||
subsurface_create(child, child->view, wlr_subsurface);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
view_child_finish(struct cg_view_child *child) {
|
|
||||||
if(!child) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(child->view != NULL) {
|
|
||||||
view_damage_child(child, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cg_view_child *subchild, *tmpchild;
|
|
||||||
wl_list_for_each_safe(subchild, tmpchild, &child->children, parent_link) {
|
|
||||||
subchild->parent = NULL;
|
|
||||||
wl_list_remove(&subchild->parent_link);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_remove(&child->link);
|
|
||||||
if(child->parent != NULL) {
|
|
||||||
wl_list_remove(&child->parent_link);
|
|
||||||
}
|
|
||||||
wl_list_remove(&child->commit.link);
|
|
||||||
wl_list_remove(&child->new_subsurface.link);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
view_child_init(struct cg_view_child *child, struct cg_view_child *parent,
|
|
||||||
struct cg_view *view, struct wlr_surface *wlr_surface) {
|
|
||||||
child->view = view;
|
|
||||||
child->parent = parent;
|
|
||||||
if(parent != NULL) {
|
|
||||||
wl_list_insert(&parent->children, &child->parent_link);
|
|
||||||
}
|
|
||||||
child->wlr_surface = wlr_surface;
|
|
||||||
wl_list_init(&child->children);
|
|
||||||
|
|
||||||
child->commit.notify = view_child_handle_commit;
|
|
||||||
wl_signal_add(&wlr_surface->events.commit, &child->commit);
|
|
||||||
child->new_subsurface.notify = view_child_handle_new_subsurface;
|
|
||||||
wl_signal_add(&wlr_surface->events.new_subsurface, &child->new_subsurface);
|
|
||||||
|
|
||||||
wl_list_insert(&view->children, &child->link);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
subsurface_destroy(struct cg_view_child *child) {
|
|
||||||
if(!child) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cg_subsurface *subsurface = (struct cg_subsurface *)child;
|
|
||||||
wl_list_remove(&subsurface->destroy.link);
|
|
||||||
view_child_finish(&subsurface->view_child);
|
|
||||||
free(subsurface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
subsurface_handle_destroy(struct wl_listener *listener, void *_data) {
|
|
||||||
struct cg_subsurface *subsurface =
|
|
||||||
wl_container_of(listener, subsurface, destroy);
|
|
||||||
struct cg_view_child *view_child = (struct cg_view_child *)subsurface;
|
|
||||||
subsurface_destroy(view_child);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
subsurface_create(struct cg_view_child *parent, struct cg_view *view,
|
|
||||||
struct wlr_subsurface *wlr_subsurface) {
|
|
||||||
struct cg_subsurface *subsurface = calloc(1, sizeof(struct cg_subsurface));
|
|
||||||
if(!subsurface) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
view_child_init(&subsurface->view_child, parent, view,
|
|
||||||
wlr_subsurface->surface);
|
|
||||||
subsurface->view_child.destroy = subsurface_destroy;
|
|
||||||
subsurface->wlr_subsurface = wlr_subsurface;
|
|
||||||
|
|
||||||
subsurface->destroy.notify = subsurface_handle_destroy;
|
|
||||||
wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_new_subsurface(struct wl_listener *listener, void *data) {
|
|
||||||
struct cg_view *view = wl_container_of(listener, view, new_subsurface);
|
|
||||||
struct wlr_subsurface *wlr_subsurface = data;
|
|
||||||
subsurface_create(NULL, view, wlr_subsurface);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
view_get_title(const struct cg_view *view) {
|
|
||||||
const char *title = view->impl->get_title(view);
|
|
||||||
if(!title) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return strndup(title, strlen(title));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
view_is_primary(const struct cg_view *view) {
|
view_is_primary(const struct cg_view *view) {
|
||||||
return view->impl->is_primary(view);
|
return view->impl->is_primary(view);
|
||||||
|
|
@ -195,28 +70,6 @@ view_is_visible(const struct cg_view *view) {
|
||||||
return view_get_tile(view) != NULL;
|
return view_get_tile(view) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
view_damage(struct cg_view *view, bool whole) {
|
|
||||||
struct cg_tile *view_tile = view_get_tile(view);
|
|
||||||
if(view_tile != NULL &&
|
|
||||||
(view->wlr_surface->current.width != view_tile->tile.width ||
|
|
||||||
view->wlr_surface->current.height != view_tile->tile.height)) {
|
|
||||||
view_maximize(view, view_tile);
|
|
||||||
}
|
|
||||||
output_damage_surface(view->workspace->output, view->wlr_surface, view->ox,
|
|
||||||
view->oy, whole);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
view_damage_part(struct cg_view *view) {
|
|
||||||
view_damage(view, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
view_damage_whole(struct cg_view *view) {
|
|
||||||
view_damage(view, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
view_activate(struct cg_view *view, bool activate) {
|
view_activate(struct cg_view *view, bool activate) {
|
||||||
if(view != NULL) {
|
if(view != NULL) {
|
||||||
|
|
@ -228,45 +81,42 @@ void
|
||||||
view_maximize(struct cg_view *view, struct cg_tile *tile) {
|
view_maximize(struct cg_view *view, struct cg_tile *tile) {
|
||||||
view->ox = tile->tile.x;
|
view->ox = tile->tile.x;
|
||||||
view->oy = tile->tile.y;
|
view->oy = tile->tile.y;
|
||||||
|
struct wlr_box box;
|
||||||
|
wlr_output_layout_get_box(view->server->output_layout,
|
||||||
|
view->workspace->output->wlr_output, &box);
|
||||||
|
wlr_scene_node_set_position(&view->scene_tree->node, view->ox + box.x,
|
||||||
|
view->oy + box.y);
|
||||||
view->impl->maximize(view, tile->tile.width, tile->tile.height);
|
view->impl->maximize(view, tile->tile.width, tile->tile.height);
|
||||||
view->tile = tile;
|
view->tile = tile;
|
||||||
}
|
wlr_scene_node_raise_to_top(&view->scene_tree->node);
|
||||||
|
|
||||||
void
|
|
||||||
view_for_each_surface(struct cg_view *view,
|
|
||||||
wlr_surface_iterator_func_t iterator, void *data) {
|
|
||||||
view->impl->for_each_surface(view, iterator, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
view_for_each_popup(struct cg_view *view, wlr_surface_iterator_func_t iterator,
|
|
||||||
void *data) {
|
|
||||||
if(!view->impl->for_each_popup) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
view->impl->for_each_popup(view, iterator, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
view_unmap(struct cg_view *view) {
|
view_unmap(struct cg_view *view) {
|
||||||
|
|
||||||
|
uint32_t id = view->id;
|
||||||
|
uint32_t tile_id = 0;
|
||||||
|
uint32_t ws = view->workspace->num;
|
||||||
|
char *output_name = view->workspace->output->wlr_output->name;
|
||||||
|
int output_id = output_get_num(view->workspace->output);
|
||||||
|
pid_t pid = view->impl->get_pid(view);
|
||||||
/* If the view is not mapped, do nothing */
|
/* If the view is not mapped, do nothing */
|
||||||
if(view->wlr_surface == NULL) {
|
if(view->wlr_surface == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cg_view_child *child, *tmp;
|
if(view->tile == NULL) {
|
||||||
wl_list_for_each_safe(child, tmp, &view->children, link) {
|
tile_id = -1;
|
||||||
child->destroy(child);
|
} else {
|
||||||
|
tile_id = view->tile->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_scene_node_destroy(&view->scene_tree->node);
|
||||||
|
|
||||||
#if CG_HAS_XWAYLAND
|
#if CG_HAS_XWAYLAND
|
||||||
if((view->type != CG_XWAYLAND_VIEW || xwayland_view_should_manage(view)))
|
if((view->type != CG_XWAYLAND_VIEW || xwayland_view_should_manage(view)))
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
struct cg_tile *view_tile = view_get_tile(view);
|
|
||||||
if(view_tile != NULL) {
|
|
||||||
wlr_output_damage_add_box(view_tile->workspace->output->damage,
|
|
||||||
&view_tile->tile);
|
|
||||||
}
|
|
||||||
struct cg_view *prev = view_get_prev_view(view);
|
struct cg_view *prev = view_get_prev_view(view);
|
||||||
if(view == view->server->seat->focused_view) {
|
if(view == view->server->seat->focused_view) {
|
||||||
seat_set_focus(view->server->seat, prev);
|
seat_set_focus(view->server->seat, prev);
|
||||||
|
|
@ -276,19 +126,13 @@ view_unmap(struct cg_view *view) {
|
||||||
seat_set_focus(view->server->seat,
|
seat_set_focus(view->server->seat,
|
||||||
view->server->seat->focused_view);
|
view->server->seat->focused_view);
|
||||||
}
|
}
|
||||||
view_tile = view_get_tile(view);
|
struct cg_tile *view_tile = view_get_tile(view);
|
||||||
if(view_tile != NULL) {
|
if(view_tile != NULL) {
|
||||||
wlr_output_damage_add_box(view_tile->workspace->output->damage,
|
workspace_tile_update_view(view_tile, prev);
|
||||||
&view_tile->tile);
|
|
||||||
view_tile->view = prev;
|
|
||||||
if(prev != NULL) {
|
|
||||||
view_maximize(prev, view_tile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if CG_HAS_XWAYLAND
|
#if CG_HAS_XWAYLAND
|
||||||
else {
|
else {
|
||||||
view_damage_whole(view);
|
|
||||||
if(view->server->seat->seat->keyboard_state.focused_surface == NULL ||
|
if(view->server->seat->seat->keyboard_state.focused_surface == NULL ||
|
||||||
view->server->seat->seat->keyboard_state.focused_surface ==
|
view->server->seat->seat->keyboard_state.focused_surface ==
|
||||||
view->wlr_surface) {
|
view->wlr_surface) {
|
||||||
|
|
@ -300,8 +144,12 @@ view_unmap(struct cg_view *view) {
|
||||||
|
|
||||||
wl_list_remove(&view->link);
|
wl_list_remove(&view->link);
|
||||||
|
|
||||||
wl_list_remove(&view->new_subsurface.link);
|
|
||||||
view->wlr_surface = NULL;
|
view->wlr_surface = NULL;
|
||||||
|
ipc_send_event(
|
||||||
|
view->workspace->server,
|
||||||
|
"{\"event_name\":\"view_unmap\",\"view_id\":%d,\"tile_id\":%d,"
|
||||||
|
"\"workspace\":%d,\"output\":\"%s\",\"output_id\":%d,\"view_pid\":%d}",
|
||||||
|
id, tile_id, ws + 1, output_name, output_id, pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -310,21 +158,12 @@ view_map(struct cg_view *view, struct wlr_surface *surface,
|
||||||
struct cg_output *output = ws->output;
|
struct cg_output *output = ws->output;
|
||||||
view->wlr_surface = surface;
|
view->wlr_surface = surface;
|
||||||
|
|
||||||
struct wlr_subsurface *subsurface;
|
view->scene_tree = wlr_scene_subsurface_tree_create(ws->scene, surface);
|
||||||
wl_list_for_each(subsurface, &view->wlr_surface->current.subsurfaces_above,
|
if(!view->scene_tree) {
|
||||||
current.link) {
|
wl_resource_post_no_memory(surface->resource);
|
||||||
subsurface_create(NULL, view, subsurface);
|
return;
|
||||||
}
|
}
|
||||||
|
view->scene_tree->node.data = view;
|
||||||
wl_list_for_each(subsurface, &view->wlr_surface->current.subsurfaces_below,
|
|
||||||
current.link) {
|
|
||||||
subsurface_create(NULL, view, subsurface);
|
|
||||||
}
|
|
||||||
|
|
||||||
view->new_subsurface.notify = handle_new_subsurface;
|
|
||||||
wl_signal_add(&view->wlr_surface->events.new_subsurface,
|
|
||||||
&view->new_subsurface);
|
|
||||||
|
|
||||||
view->workspace = ws;
|
view->workspace = ws;
|
||||||
|
|
||||||
#if CG_HAS_XWAYLAND
|
#if CG_HAS_XWAYLAND
|
||||||
|
|
@ -332,14 +171,30 @@ view_map(struct cg_view *view, struct wlr_surface *surface,
|
||||||
their own (x,y) coordinates in handle_wayland_surface_map. */
|
their own (x,y) coordinates in handle_wayland_surface_map. */
|
||||||
if(view->type == CG_XWAYLAND_VIEW && !xwayland_view_should_manage(view)) {
|
if(view->type == CG_XWAYLAND_VIEW && !xwayland_view_should_manage(view)) {
|
||||||
wl_list_insert(&ws->unmanaged_views, &view->link);
|
wl_list_insert(&ws->unmanaged_views, &view->link);
|
||||||
|
struct wlr_box box;
|
||||||
|
wlr_output_layout_get_box(view->server->output_layout,
|
||||||
|
view->workspace->output->wlr_output, &box);
|
||||||
|
wlr_scene_node_set_position(&view->scene_tree->node, view->ox + box.x,
|
||||||
|
view->oy + box.y);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
view->tile = view->workspace->focused_tile;
|
|
||||||
view_maximize(view, view->tile);
|
|
||||||
wl_list_insert(&ws->views, &view->link);
|
wl_list_insert(&ws->views, &view->link);
|
||||||
}
|
}
|
||||||
seat_set_focus(output->server->seat, view);
|
seat_set_focus(output->server->seat, view);
|
||||||
|
int tile_id = 0;
|
||||||
|
if(view->tile == NULL) {
|
||||||
|
tile_id = -1;
|
||||||
|
} else {
|
||||||
|
tile_id = view->tile->id;
|
||||||
|
}
|
||||||
|
ipc_send_event(
|
||||||
|
output->server,
|
||||||
|
"{\"event_name\":\"view_map\",\"view_id\":%d,\"tile_id\":%d,"
|
||||||
|
"\"workspace\":%d,\"output\":\"%s\",\"output_id\":%d,\"view_pid\":%d}",
|
||||||
|
view->id, tile_id, view->workspace->num + 1,
|
||||||
|
view->workspace->output->wlr_output->name,
|
||||||
|
output_get_num(view->workspace->output), view->impl->get_pid(view));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -363,12 +218,6 @@ view_init(struct cg_view *view, enum cg_view_type type,
|
||||||
view->server = server;
|
view->server = server;
|
||||||
view->type = type;
|
view->type = type;
|
||||||
view->impl = impl;
|
view->impl = impl;
|
||||||
|
view->id = server->views_curr_id;
|
||||||
wl_list_init(&view->children);
|
++server->views_curr_id;
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_surface *
|
|
||||||
view_wlr_surface_at(const struct cg_view *view, double sx, double sy,
|
|
||||||
double *sub_x, double *sub_y) {
|
|
||||||
return view->impl->wlr_surface_at(view, sx, sy, sub_x, sub_y);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
65
view.h
65
view.h
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef CG_VIEW_H
|
#ifndef CG_VIEW_H
|
||||||
#define CG_VIEW_H
|
#define CG_VIEW_H
|
||||||
|
|
||||||
|
|
@ -5,7 +8,7 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/types/wlr_surface.h>
|
#include <wlr/types/wlr_compositor.h>
|
||||||
|
|
||||||
struct cg_server;
|
struct cg_server;
|
||||||
struct wlr_box;
|
struct wlr_box;
|
||||||
|
|
@ -20,10 +23,10 @@ enum cg_view_type {
|
||||||
struct cg_view {
|
struct cg_view {
|
||||||
struct cg_workspace *workspace;
|
struct cg_workspace *workspace;
|
||||||
struct cg_server *server;
|
struct cg_server *server;
|
||||||
struct wl_list link; // server::views
|
struct wl_list link; // server::views
|
||||||
struct wl_list children; // cg_view_child::link
|
|
||||||
struct wlr_surface *wlr_surface;
|
struct wlr_surface *wlr_surface;
|
||||||
struct cg_tile *tile;
|
struct cg_tile *tile;
|
||||||
|
struct wlr_scene_tree *scene_tree;
|
||||||
|
|
||||||
/* The view has a position in output coordinates. */
|
/* The view has a position in output coordinates. */
|
||||||
int ox, oy;
|
int ox, oy;
|
||||||
|
|
@ -31,48 +34,18 @@ struct cg_view {
|
||||||
enum cg_view_type type;
|
enum cg_view_type type;
|
||||||
const struct cg_view_impl *impl;
|
const struct cg_view_impl *impl;
|
||||||
|
|
||||||
struct wl_listener new_subsurface;
|
uint32_t id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_view_impl {
|
struct cg_view_impl {
|
||||||
char *(*get_title)(const struct cg_view *view);
|
pid_t (*get_pid)(const struct cg_view *view);
|
||||||
bool (*is_primary)(const struct cg_view *view);
|
bool (*is_primary)(const struct cg_view *view);
|
||||||
void (*activate)(struct cg_view *view, bool activate);
|
void (*activate)(struct cg_view *view, bool activate);
|
||||||
void (*close)(struct cg_view *view);
|
void (*close)(struct cg_view *view);
|
||||||
void (*maximize)(struct cg_view *view, int width, int height);
|
void (*maximize)(struct cg_view *view, int width, int height);
|
||||||
void (*destroy)(struct cg_view *view);
|
void (*destroy)(struct cg_view *view);
|
||||||
void (*for_each_surface)(struct cg_view *view,
|
|
||||||
wlr_surface_iterator_func_t iterator, void *data);
|
|
||||||
void (*for_each_popup)(struct cg_view *view,
|
|
||||||
wlr_surface_iterator_func_t iterator, void *data);
|
|
||||||
struct wlr_surface *(*wlr_surface_at)(const struct cg_view *view, double sx,
|
|
||||||
double sy, double *sub_x,
|
|
||||||
double *sub_y);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_view_child {
|
|
||||||
struct cg_view *view;
|
|
||||||
struct cg_view_child *parent;
|
|
||||||
struct wl_list children;
|
|
||||||
struct wlr_surface *wlr_surface;
|
|
||||||
struct wl_list link;
|
|
||||||
struct wl_list parent_link;
|
|
||||||
|
|
||||||
struct wl_listener commit;
|
|
||||||
struct wl_listener new_subsurface;
|
|
||||||
|
|
||||||
void (*destroy)(struct cg_view_child *child);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cg_subsurface {
|
|
||||||
struct cg_view_child view_child;
|
|
||||||
struct wlr_subsurface *wlr_subsurface;
|
|
||||||
|
|
||||||
struct wl_listener destroy;
|
|
||||||
};
|
|
||||||
|
|
||||||
char *
|
|
||||||
view_get_title(const struct cg_view *view);
|
|
||||||
struct cg_tile *
|
struct cg_tile *
|
||||||
view_get_tile(const struct cg_view *view);
|
view_get_tile(const struct cg_view *view);
|
||||||
bool
|
bool
|
||||||
|
|
@ -80,20 +53,8 @@ view_is_primary(const struct cg_view *view);
|
||||||
bool
|
bool
|
||||||
view_is_visible(const struct cg_view *view);
|
view_is_visible(const struct cg_view *view);
|
||||||
void
|
void
|
||||||
view_damage_part(struct cg_view *view);
|
|
||||||
void
|
|
||||||
view_damage_whole(struct cg_view *view);
|
|
||||||
void
|
|
||||||
view_damage_child(struct cg_view_child *view, bool whole);
|
|
||||||
void
|
|
||||||
view_activate(struct cg_view *view, bool activate);
|
view_activate(struct cg_view *view, bool activate);
|
||||||
void
|
void
|
||||||
view_for_each_surface(struct cg_view *view,
|
|
||||||
wlr_surface_iterator_func_t iterator, void *data);
|
|
||||||
void
|
|
||||||
view_for_each_popup(struct cg_view *view, wlr_surface_iterator_func_t iterator,
|
|
||||||
void *data);
|
|
||||||
void
|
|
||||||
view_unmap(struct cg_view *view);
|
view_unmap(struct cg_view *view);
|
||||||
void
|
void
|
||||||
view_maximize(struct cg_view *view, struct cg_tile *tile);
|
view_maximize(struct cg_view *view, struct cg_tile *tile);
|
||||||
|
|
@ -105,16 +66,6 @@ view_destroy(struct cg_view *view);
|
||||||
void
|
void
|
||||||
view_init(struct cg_view *view, enum cg_view_type type,
|
view_init(struct cg_view *view, enum cg_view_type type,
|
||||||
const struct cg_view_impl *impl, struct cg_server *server);
|
const struct cg_view_impl *impl, struct cg_server *server);
|
||||||
|
|
||||||
struct wlr_surface *
|
|
||||||
view_wlr_surface_at(const struct cg_view *view, double sx, double sy,
|
|
||||||
double *sub_x, double *sub_y);
|
|
||||||
|
|
||||||
void
|
|
||||||
view_child_finish(struct cg_view_child *child);
|
|
||||||
void
|
|
||||||
view_child_init(struct cg_view_child *child, struct cg_view_child *parent,
|
|
||||||
struct cg_view *view, struct wlr_surface *wlr_surface);
|
|
||||||
struct cg_view *
|
struct cg_view *
|
||||||
view_get_prev_view(struct cg_view *view);
|
view_get_prev_view(struct cg_view *view);
|
||||||
|
|
||||||
|
|
|
||||||
74
workspace.c
74
workspace.c
|
|
@ -1,24 +1,34 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2022 The Cagebreak Authors
|
|
||||||
* Copyright (C) 2018-2019 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
|
#include <wlr/types/wlr_scene.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
|
#include "seat.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
#include "view.h"
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
workspace_tile_update_view(struct cg_tile *tile, struct cg_view *view) {
|
||||||
|
if(tile->view != NULL) {
|
||||||
|
wlr_scene_node_set_enabled(&tile->view->scene_tree->node, false);
|
||||||
|
tile->view->tile = NULL;
|
||||||
|
}
|
||||||
|
tile->view = view;
|
||||||
|
if(view != NULL) {
|
||||||
|
view_maximize(view, tile);
|
||||||
|
wlr_scene_node_set_enabled(&view->scene_tree->node, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if CG_HAS_FANALYZE
|
#if CG_HAS_FANALYZE
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
|
#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
|
||||||
|
|
@ -26,7 +36,8 @@
|
||||||
int
|
int
|
||||||
full_screen_workspace_tiles(struct wlr_output_layout *layout,
|
full_screen_workspace_tiles(struct wlr_output_layout *layout,
|
||||||
struct wlr_output *output,
|
struct wlr_output *output,
|
||||||
struct cg_workspace *workspace) {
|
struct cg_workspace *workspace,
|
||||||
|
uint32_t *tiles_curr_id) {
|
||||||
workspace->focused_tile = calloc(1, sizeof(struct cg_tile));
|
workspace->focused_tile = calloc(1, sizeof(struct cg_tile));
|
||||||
if(!workspace->focused_tile) {
|
if(!workspace->focused_tile) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -36,10 +47,13 @@ full_screen_workspace_tiles(struct wlr_output_layout *layout,
|
||||||
workspace->focused_tile->prev = workspace->focused_tile;
|
workspace->focused_tile->prev = workspace->focused_tile;
|
||||||
workspace->focused_tile->tile.x = 0;
|
workspace->focused_tile->tile.x = 0;
|
||||||
workspace->focused_tile->tile.y = 0;
|
workspace->focused_tile->tile.y = 0;
|
||||||
struct wlr_box *output_box = wlr_output_layout_get_box(layout, output);
|
struct wlr_box output_box;
|
||||||
workspace->focused_tile->tile.width = output_box->width;
|
wlr_output_layout_get_box(layout, output, &output_box);
|
||||||
workspace->focused_tile->tile.height = output_box->height;
|
workspace->focused_tile->tile.width = output_box.width;
|
||||||
workspace->focused_tile->view = NULL;
|
workspace->focused_tile->tile.height = output_box.height;
|
||||||
|
workspace_tile_update_view(workspace->focused_tile, NULL);
|
||||||
|
workspace->focused_tile->id = *tiles_curr_id;
|
||||||
|
++(*tiles_curr_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#if CG_HAS_FANALYZE
|
#if CG_HAS_FANALYZE
|
||||||
|
|
@ -52,9 +66,18 @@ full_screen_workspace(struct cg_output *output) {
|
||||||
if(!workspace) {
|
if(!workspace) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
struct wlr_scene_output *scene_output =
|
||||||
|
wlr_scene_get_scene_output(output->server->scene, output->wlr_output);
|
||||||
|
if(scene_output == NULL) {
|
||||||
|
free(workspace);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
workspace->server = output->server;
|
workspace->server = output->server;
|
||||||
|
workspace->num = -1;
|
||||||
|
workspace->scene = wlr_scene_tree_create(&scene_output->scene->tree);
|
||||||
if(full_screen_workspace_tiles(output->server->output_layout,
|
if(full_screen_workspace_tiles(output->server->output_layout,
|
||||||
output->wlr_output, workspace) != 0) {
|
output->wlr_output, workspace,
|
||||||
|
&output->server->tiles_curr_id) != 0) {
|
||||||
free(workspace);
|
free(workspace);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -72,13 +95,18 @@ workspace_focus_tile(struct cg_workspace *ws, struct cg_tile *tile) {
|
||||||
}
|
}
|
||||||
box->x = tile->tile.x + tile->tile.width / 2;
|
box->x = tile->tile.x + tile->tile.width / 2;
|
||||||
box->y = tile->tile.y + tile->tile.height / 2;
|
box->y = tile->tile.y + tile->tile.height / 2;
|
||||||
message_printf_pos(ws->output, box, CG_MESSAGE_CENTER, "Current frame");
|
message_printf_pos(ws->output, box, CG_MESSAGE_CENTER, "Current tile");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
workspace_free_tiles(struct cg_workspace *workspace) {
|
workspace_free_tiles(struct cg_workspace *workspace) {
|
||||||
workspace->focused_tile->prev->next = NULL;
|
workspace->focused_tile->prev->next = NULL;
|
||||||
while(workspace->focused_tile != NULL) {
|
while(workspace->focused_tile != NULL) {
|
||||||
|
if(workspace->output->server->running &&
|
||||||
|
workspace->output->server->seat->cursor_tile ==
|
||||||
|
workspace->focused_tile) {
|
||||||
|
workspace->output->server->seat->cursor_tile = NULL;
|
||||||
|
}
|
||||||
struct cg_tile *next = workspace->focused_tile->next;
|
struct cg_tile *next = workspace->focused_tile->next;
|
||||||
free(workspace->focused_tile);
|
free(workspace->focused_tile);
|
||||||
workspace->focused_tile = next;
|
workspace->focused_tile = next;
|
||||||
|
|
@ -90,3 +118,19 @@ workspace_free(struct cg_workspace *workspace) {
|
||||||
workspace_free_tiles(workspace);
|
workspace_free_tiles(workspace);
|
||||||
free(workspace);
|
free(workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
workspace_focus(struct cg_output *outp, int ws) {
|
||||||
|
if(ws >= outp->server->nws) {
|
||||||
|
wlr_log(WLR_ERROR,
|
||||||
|
"Attempt to focus workspace %d, but only %d workspaces are "
|
||||||
|
"available.",
|
||||||
|
ws, outp->server->nws);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wlr_scene_node_place_above(
|
||||||
|
&outp->bg->node, &outp->workspaces[outp->curr_workspace]->scene->node);
|
||||||
|
wlr_scene_node_place_above(&outp->workspaces[ws]->scene->node,
|
||||||
|
&outp->bg->node);
|
||||||
|
outp->curr_workspace = ws;
|
||||||
|
}
|
||||||
|
|
|
||||||
13
workspace.h
13
workspace.h
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef CG_WORKSPACE_H
|
#ifndef CG_WORKSPACE_H
|
||||||
#define CG_WORKSPACE_H
|
#define CG_WORKSPACE_H
|
||||||
|
|
||||||
|
|
@ -12,6 +15,7 @@ struct cg_tile {
|
||||||
struct cg_view *view;
|
struct cg_view *view;
|
||||||
struct cg_tile *next;
|
struct cg_tile *next;
|
||||||
struct cg_tile *prev;
|
struct cg_tile *prev;
|
||||||
|
uint32_t id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_workspace {
|
struct cg_workspace {
|
||||||
|
|
@ -19,8 +23,10 @@ struct cg_workspace {
|
||||||
struct wl_list views;
|
struct wl_list views;
|
||||||
struct wl_list unmanaged_views;
|
struct wl_list unmanaged_views;
|
||||||
struct cg_output *output;
|
struct cg_output *output;
|
||||||
|
struct wlr_scene_tree *scene;
|
||||||
|
|
||||||
struct cg_tile *focused_tile;
|
struct cg_tile *focused_tile;
|
||||||
|
uint32_t num;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_workspace *
|
struct cg_workspace *
|
||||||
|
|
@ -28,12 +34,17 @@ full_screen_workspace(struct cg_output *output);
|
||||||
int
|
int
|
||||||
full_screen_workspace_tiles(struct wlr_output_layout *layout,
|
full_screen_workspace_tiles(struct wlr_output_layout *layout,
|
||||||
struct wlr_output *output,
|
struct wlr_output *output,
|
||||||
struct cg_workspace *workspace);
|
struct cg_workspace *workspace,
|
||||||
|
uint32_t *tiles_curr_id);
|
||||||
void
|
void
|
||||||
workspace_free_tiles(struct cg_workspace *workspace);
|
workspace_free_tiles(struct cg_workspace *workspace);
|
||||||
void
|
void
|
||||||
workspace_free(struct cg_workspace *workspace);
|
workspace_free(struct cg_workspace *workspace);
|
||||||
void
|
void
|
||||||
workspace_focus_tile(struct cg_workspace *ws, struct cg_tile *tile);
|
workspace_focus_tile(struct cg_workspace *ws, struct cg_tile *tile);
|
||||||
|
void
|
||||||
|
workspace_focus(struct cg_output *outp, int ws);
|
||||||
|
void
|
||||||
|
workspace_tile_update_view(struct cg_tile *tile, struct cg_view *view);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
327
xdg_shell.c
327
xdg_shell.c
|
|
@ -1,17 +1,13 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2022 The Cagebreak Authors
|
|
||||||
* Copyright (C) 2018-2019 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
|
#include <wlr/types/wlr_scene.h>
|
||||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||||
#include <wlr/types/wlr_xdg_shell.h>
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
#include <wlr/util/box.h>
|
#include <wlr/util/box.h>
|
||||||
|
|
@ -24,8 +20,29 @@
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
#include "xdg_shell.h"
|
#include "xdg_shell.h"
|
||||||
|
|
||||||
|
static struct cg_view *
|
||||||
|
popup_get_view(struct wlr_xdg_popup *popup) {
|
||||||
|
while(true) {
|
||||||
|
if(popup->parent == NULL ||
|
||||||
|
!wlr_surface_is_xdg_surface(popup->parent)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
struct wlr_xdg_surface *xdg_surface =
|
||||||
|
wlr_xdg_surface_from_wlr_surface(popup->parent);
|
||||||
|
switch(xdg_surface->role) {
|
||||||
|
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
|
||||||
|
return xdg_surface->data;
|
||||||
|
case WLR_XDG_SURFACE_ROLE_POPUP:
|
||||||
|
popup = xdg_surface->popup;
|
||||||
|
break;
|
||||||
|
case WLR_XDG_SURFACE_ROLE_NONE:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xdg_decoration_handle_destroy(struct wl_listener *listener, void *_data) {
|
xdg_decoration_handle_destroy(struct wl_listener *listener, void *data) {
|
||||||
struct cg_xdg_decoration *xdg_decoration =
|
struct cg_xdg_decoration *xdg_decoration =
|
||||||
wl_container_of(listener, xdg_decoration, destroy);
|
wl_container_of(listener, xdg_decoration, destroy);
|
||||||
|
|
||||||
|
|
@ -53,100 +70,25 @@ xdg_decoration_handle_request_mode(struct wl_listener *listener, void *_data) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xdg_popup_destroy(struct cg_view_child *child) {
|
popup_unconstrain(struct cg_view *view, struct wlr_xdg_popup *popup) {
|
||||||
if(!child) {
|
struct wlr_box *popup_box = &popup->current.geometry;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
view_damage_child(child, true);
|
|
||||||
struct cg_xdg_popup *popup = (struct cg_xdg_popup *)child;
|
|
||||||
wl_list_remove(&popup->destroy.link);
|
|
||||||
wl_list_remove(&popup->map.link);
|
|
||||||
wl_list_remove(&popup->unmap.link);
|
|
||||||
wl_list_remove(&popup->new_popup.link);
|
|
||||||
view_child_finish(&popup->view_child);
|
|
||||||
free(popup);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_xdg_popup_map(struct wl_listener *listener, void *_data) {
|
|
||||||
struct cg_xdg_popup *popup = wl_container_of(listener, popup, map);
|
|
||||||
view_damage_whole(popup->view_child.view);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_xdg_popup_unmap(struct wl_listener *listener, void *_data) {
|
|
||||||
struct cg_xdg_popup *popup = wl_container_of(listener, popup, unmap);
|
|
||||||
view_damage_whole(popup->view_child.view);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_xdg_popup_destroy(struct wl_listener *listener, void *_data) {
|
|
||||||
struct cg_xdg_popup *popup = wl_container_of(listener, popup, destroy);
|
|
||||||
struct cg_view_child *view_child = (struct cg_view_child *)popup;
|
|
||||||
xdg_popup_destroy(view_child);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
xdg_popup_create(struct cg_view *view, struct wlr_xdg_popup *wlr_popup);
|
|
||||||
|
|
||||||
static void
|
|
||||||
popup_handle_new_xdg_popup(struct wl_listener *listener, void *data) {
|
|
||||||
struct cg_xdg_popup *popup = wl_container_of(listener, popup, new_popup);
|
|
||||||
struct wlr_xdg_popup *wlr_popup = data;
|
|
||||||
xdg_popup_create(popup->view_child.view, wlr_popup);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
popup_unconstrain(struct cg_xdg_popup *popup) {
|
|
||||||
struct cg_view *view = popup->view_child.view;
|
|
||||||
struct wlr_box *popup_box = &popup->wlr_popup->geometry;
|
|
||||||
|
|
||||||
struct wlr_output_layout *output_layout = view->server->output_layout;
|
struct wlr_output_layout *output_layout = view->server->output_layout;
|
||||||
struct wlr_box *view_output_box = wlr_output_layout_get_box(
|
struct wlr_box view_output_box;
|
||||||
output_layout, view->workspace->output->wlr_output);
|
wlr_output_layout_get_box(
|
||||||
|
output_layout, view->workspace->output->wlr_output, &view_output_box);
|
||||||
struct wlr_output *wlr_output = wlr_output_layout_output_at(
|
struct wlr_output *wlr_output = wlr_output_layout_output_at(
|
||||||
output_layout, view_output_box->x + view->ox + popup_box->x,
|
output_layout, view_output_box.x + view->ox + popup_box->x,
|
||||||
view_output_box->y + view->oy + popup_box->y);
|
view_output_box.y + view->oy + popup_box->y);
|
||||||
struct wlr_box *output_box =
|
struct wlr_box output_box;
|
||||||
wlr_output_layout_get_box(output_layout, wlr_output);
|
wlr_output_layout_get_box(output_layout, wlr_output, &output_box);
|
||||||
|
|
||||||
struct wlr_box output_toplevel_box = {.x = -view->ox,
|
struct wlr_box output_toplevel_box = {.x = -view->ox,
|
||||||
.y = -view->oy,
|
.y = -view->oy,
|
||||||
.width = output_box->width,
|
.width = output_box.width,
|
||||||
.height = output_box->height};
|
.height = output_box.height};
|
||||||
|
|
||||||
wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup, &output_toplevel_box);
|
wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box);
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
xdg_popup_create(struct cg_view *view, struct wlr_xdg_popup *wlr_popup) {
|
|
||||||
struct cg_xdg_popup *popup = calloc(1, sizeof(struct cg_xdg_popup));
|
|
||||||
if(!popup) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
popup->wlr_popup = wlr_popup;
|
|
||||||
view_child_init(&popup->view_child, NULL, view, wlr_popup->base->surface);
|
|
||||||
popup->view_child.destroy = xdg_popup_destroy;
|
|
||||||
popup->destroy.notify = handle_xdg_popup_destroy;
|
|
||||||
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
|
|
||||||
popup->map.notify = handle_xdg_popup_map;
|
|
||||||
wl_signal_add(&wlr_popup->base->events.map, &popup->map);
|
|
||||||
popup->unmap.notify = handle_xdg_popup_unmap;
|
|
||||||
wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
|
|
||||||
popup->new_popup.notify = popup_handle_new_xdg_popup;
|
|
||||||
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
|
|
||||||
|
|
||||||
popup_unconstrain(popup);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_new_xdg_popup(struct wl_listener *listener, void *data) {
|
|
||||||
struct cg_xdg_shell_view *xdg_shell_view =
|
|
||||||
wl_container_of(listener, xdg_shell_view, new_popup);
|
|
||||||
struct wlr_xdg_popup *wlr_popup = data;
|
|
||||||
xdg_popup_create(&xdg_shell_view->view, wlr_popup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct cg_xdg_shell_view *
|
static struct cg_xdg_shell_view *
|
||||||
|
|
@ -159,27 +101,29 @@ xdg_shell_view_from_const_view(const struct cg_view *view) {
|
||||||
return (const struct cg_xdg_shell_view *)view;
|
return (const struct cg_xdg_shell_view *)view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static pid_t
|
||||||
get_title(const struct cg_view *view) {
|
get_pid(const struct cg_view *view) {
|
||||||
const struct cg_xdg_shell_view *xdg_shell_view =
|
pid_t pid;
|
||||||
xdg_shell_view_from_const_view(view);
|
struct wl_client *client =
|
||||||
return xdg_shell_view->xdg_surface->toplevel->title;
|
wl_resource_get_client(view->wlr_surface->resource);
|
||||||
|
wl_client_get_credentials(client, &pid, NULL, NULL);
|
||||||
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_primary(const struct cg_view *view) {
|
is_primary(const struct cg_view *view) {
|
||||||
const struct cg_xdg_shell_view *xdg_shell_view =
|
const struct cg_xdg_shell_view *xdg_shell_view =
|
||||||
xdg_shell_view_from_const_view(view);
|
xdg_shell_view_from_const_view(view);
|
||||||
struct wlr_xdg_surface *parent =
|
struct wlr_xdg_toplevel *parent =
|
||||||
xdg_shell_view->xdg_surface->toplevel->parent;
|
xdg_shell_view->xdg_surface->toplevel->parent;
|
||||||
/* FIXME: role is 0? */
|
return parent == NULL;
|
||||||
return parent == NULL; /*&& role == WLR_XDG_SURFACE_ROLE_TOPLEVEL */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
activate(struct cg_view *view, bool activate) {
|
activate(struct cg_view *view, bool activate) {
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||||
wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_surface, activate);
|
wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_surface->toplevel,
|
||||||
|
activate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -187,17 +131,18 @@ close(struct cg_view *view) {
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||||
struct wlr_xdg_surface *surface = xdg_shell_view->xdg_surface;
|
struct wlr_xdg_surface *surface = xdg_shell_view->xdg_surface;
|
||||||
if(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
|
if(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
|
||||||
wlr_xdg_toplevel_send_close(surface);
|
wlr_xdg_toplevel_send_close(surface->toplevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
maximize(struct cg_view *view, int width, int height) {
|
maximize(struct cg_view *view, int width, int height) {
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||||
wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_surface, width, height);
|
wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_surface->toplevel, width,
|
||||||
|
height);
|
||||||
enum wlr_edges edges =
|
enum wlr_edges edges =
|
||||||
WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM;
|
WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM;
|
||||||
wlr_xdg_toplevel_set_tiled(xdg_shell_view->xdg_surface, edges);
|
wlr_xdg_toplevel_set_tiled(xdg_shell_view->xdg_surface->toplevel, edges);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -206,47 +151,25 @@ destroy(struct cg_view *view) {
|
||||||
free(xdg_shell_view);
|
free(xdg_shell_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator,
|
|
||||||
void *data) {
|
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
|
||||||
wlr_xdg_surface_for_each_surface(xdg_shell_view->xdg_surface, iterator,
|
|
||||||
data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
for_each_popup(struct cg_view *view, wlr_surface_iterator_func_t iterator,
|
|
||||||
void *data) {
|
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
|
||||||
wlr_xdg_surface_for_each_popup_surface(xdg_shell_view->xdg_surface,
|
|
||||||
iterator, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_surface *
|
|
||||||
wlr_surface_at(const struct cg_view *view, double sx, double sy, double *sub_x,
|
|
||||||
double *sub_y) {
|
|
||||||
const struct cg_xdg_shell_view *xdg_shell_view =
|
|
||||||
xdg_shell_view_from_const_view(view);
|
|
||||||
return wlr_xdg_surface_surface_at(xdg_shell_view->xdg_surface, sx, sy,
|
|
||||||
sub_x, sub_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_xdg_shell_surface_request_fullscreen(struct wl_listener *listener,
|
handle_xdg_shell_surface_request_fullscreen(struct wl_listener *listener,
|
||||||
void *data) {
|
void *data) {
|
||||||
struct cg_xdg_shell_view *xdg_shell_view =
|
struct cg_xdg_shell_view *xdg_shell_view =
|
||||||
wl_container_of(listener, xdg_shell_view, request_fullscreen);
|
wl_container_of(listener, xdg_shell_view, request_fullscreen);
|
||||||
struct wlr_xdg_toplevel_set_fullscreen_event *event = data;
|
|
||||||
wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_surface,
|
|
||||||
event->fullscreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
/**
|
||||||
handle_xdg_shell_surface_commit(struct wl_listener *listener, void *_data) {
|
* Certain clients do not like figuring out their own window geometry if
|
||||||
struct cg_xdg_shell_view *xdg_shell_view =
|
* they display in fullscreen mode, so we set it here.
|
||||||
wl_container_of(listener, xdg_shell_view, commit);
|
*/
|
||||||
struct cg_view *view = &xdg_shell_view->view;
|
struct wlr_box layout_box;
|
||||||
view_damage_part(view);
|
wlr_output_layout_get_box(xdg_shell_view->view.server->output_layout, NULL,
|
||||||
|
&layout_box);
|
||||||
|
wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_surface->toplevel,
|
||||||
|
layout_box.width, layout_box.height);
|
||||||
|
|
||||||
|
wlr_xdg_toplevel_set_fullscreen(
|
||||||
|
xdg_shell_view->xdg_surface->toplevel,
|
||||||
|
xdg_shell_view->xdg_surface->toplevel->requested.fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -255,9 +178,6 @@ handle_xdg_shell_surface_unmap(struct wl_listener *listener, void *_data) {
|
||||||
wl_container_of(listener, xdg_shell_view, unmap);
|
wl_container_of(listener, xdg_shell_view, unmap);
|
||||||
struct cg_view *view = &xdg_shell_view->view;
|
struct cg_view *view = &xdg_shell_view->view;
|
||||||
|
|
||||||
wl_list_remove(&xdg_shell_view->new_popup.link);
|
|
||||||
wl_list_remove(&xdg_shell_view->commit.link);
|
|
||||||
|
|
||||||
view_unmap(view);
|
view_unmap(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,17 +187,9 @@ handle_xdg_shell_surface_map(struct wl_listener *listener, void *_data) {
|
||||||
wl_container_of(listener, xdg_shell_view, map);
|
wl_container_of(listener, xdg_shell_view, map);
|
||||||
struct cg_view *view = &xdg_shell_view->view;
|
struct cg_view *view = &xdg_shell_view->view;
|
||||||
|
|
||||||
xdg_shell_view->commit.notify = handle_xdg_shell_surface_commit;
|
|
||||||
wl_signal_add(&xdg_shell_view->xdg_surface->surface->events.commit,
|
|
||||||
&xdg_shell_view->commit);
|
|
||||||
xdg_shell_view->new_popup.notify = handle_new_xdg_popup;
|
|
||||||
wl_signal_add(&xdg_shell_view->xdg_surface->events.new_popup,
|
|
||||||
&xdg_shell_view->new_popup);
|
|
||||||
|
|
||||||
view_map(view, xdg_shell_view->xdg_surface->surface,
|
view_map(view, xdg_shell_view->xdg_surface->surface,
|
||||||
view->server->curr_output
|
view->server->curr_output
|
||||||
->workspaces[view->server->curr_output->curr_workspace]);
|
->workspaces[view->server->curr_output->curr_workspace]);
|
||||||
view_damage_whole(view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -295,17 +207,13 @@ handle_xdg_shell_surface_destroy(struct wl_listener *listener, void *_data) {
|
||||||
view_destroy(view);
|
view_destroy(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct cg_view_impl xdg_shell_view_impl = {
|
static const struct cg_view_impl xdg_shell_view_impl = {.get_pid = get_pid,
|
||||||
.get_title = get_title,
|
.is_primary =
|
||||||
.is_primary = is_primary,
|
is_primary,
|
||||||
.activate = activate,
|
.activate = activate,
|
||||||
.close = close,
|
.close = close,
|
||||||
.maximize = maximize,
|
.maximize = maximize,
|
||||||
.destroy = destroy,
|
.destroy = destroy};
|
||||||
.for_each_surface = for_each_surface,
|
|
||||||
.for_each_popup = for_each_popup,
|
|
||||||
.wlr_surface_at = wlr_surface_at,
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
void
|
||||||
handle_xdg_shell_surface_new(struct wl_listener *listener, void *data) {
|
handle_xdg_shell_surface_new(struct wl_listener *listener, void *data) {
|
||||||
|
|
@ -313,31 +221,72 @@ handle_xdg_shell_surface_new(struct wl_listener *listener, void *data) {
|
||||||
wl_container_of(listener, server, new_xdg_shell_surface);
|
wl_container_of(listener, server, new_xdg_shell_surface);
|
||||||
struct wlr_xdg_surface *xdg_surface = data;
|
struct wlr_xdg_surface *xdg_surface = data;
|
||||||
|
|
||||||
if(xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
|
switch(xdg_surface->role) {
|
||||||
return;
|
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:;
|
||||||
|
struct cg_xdg_shell_view *xdg_shell_view =
|
||||||
|
calloc(1, sizeof(struct cg_xdg_shell_view));
|
||||||
|
if(!xdg_shell_view) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to allocate XDG Shell view");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
view_init(&xdg_shell_view->view, CG_XDG_SHELL_VIEW,
|
||||||
|
&xdg_shell_view_impl, server);
|
||||||
|
|
||||||
|
xdg_shell_view->xdg_surface = xdg_surface;
|
||||||
|
|
||||||
|
xdg_shell_view->map.notify = handle_xdg_shell_surface_map;
|
||||||
|
wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
|
||||||
|
xdg_shell_view->unmap.notify = handle_xdg_shell_surface_unmap;
|
||||||
|
wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap);
|
||||||
|
xdg_shell_view->destroy.notify = handle_xdg_shell_surface_destroy;
|
||||||
|
wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy);
|
||||||
|
xdg_shell_view->request_fullscreen.notify =
|
||||||
|
handle_xdg_shell_surface_request_fullscreen;
|
||||||
|
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
|
||||||
|
&xdg_shell_view->request_fullscreen);
|
||||||
|
|
||||||
|
xdg_surface->data = xdg_shell_view;
|
||||||
|
break;
|
||||||
|
case WLR_XDG_SURFACE_ROLE_POPUP:;
|
||||||
|
struct wlr_xdg_popup *popup = xdg_surface->popup;
|
||||||
|
struct cg_view *view = popup_get_view(popup);
|
||||||
|
if(view == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_scene_tree *parent_scene_tree = NULL;
|
||||||
|
struct wlr_xdg_surface *parent =
|
||||||
|
wlr_xdg_surface_from_wlr_surface(popup->parent);
|
||||||
|
switch(parent->role) {
|
||||||
|
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:;
|
||||||
|
parent_scene_tree = view->scene_tree;
|
||||||
|
break;
|
||||||
|
case WLR_XDG_SURFACE_ROLE_POPUP:
|
||||||
|
parent_scene_tree = parent->data;
|
||||||
|
break;
|
||||||
|
case WLR_XDG_SURFACE_ROLE_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(parent_scene_tree == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_scene_tree *popup_scene_tree =
|
||||||
|
wlr_scene_xdg_surface_create(parent_scene_tree, xdg_surface);
|
||||||
|
if(popup_scene_tree == NULL) {
|
||||||
|
wlr_log(WLR_ERROR,
|
||||||
|
"Failed to allocate scene-graph node for XDG popup");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
popup_unconstrain(view, popup);
|
||||||
|
|
||||||
|
xdg_surface->data = popup_scene_tree;
|
||||||
|
break;
|
||||||
|
case WLR_XDG_SURFACE_ROLE_NONE:
|
||||||
|
assert(false); // unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cg_xdg_shell_view *xdg_shell_view =
|
|
||||||
calloc(1, sizeof(struct cg_xdg_shell_view));
|
|
||||||
if(!xdg_shell_view) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate XDG Shell view");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
view_init(&xdg_shell_view->view, CG_XDG_SHELL_VIEW, &xdg_shell_view_impl,
|
|
||||||
server);
|
|
||||||
|
|
||||||
xdg_shell_view->xdg_surface = xdg_surface;
|
|
||||||
|
|
||||||
xdg_shell_view->map.notify = handle_xdg_shell_surface_map;
|
|
||||||
wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
|
|
||||||
xdg_shell_view->unmap.notify = handle_xdg_shell_surface_unmap;
|
|
||||||
wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap);
|
|
||||||
xdg_shell_view->destroy.notify = handle_xdg_shell_surface_destroy;
|
|
||||||
wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy);
|
|
||||||
xdg_shell_view->request_fullscreen.notify =
|
|
||||||
handle_xdg_shell_surface_request_fullscreen;
|
|
||||||
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
|
|
||||||
&xdg_shell_view->request_fullscreen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
15
xdg_shell.h
15
xdg_shell.h
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef CG_XDG_SHELL_H
|
#ifndef CG_XDG_SHELL_H
|
||||||
#define CG_XDG_SHELL_H
|
#define CG_XDG_SHELL_H
|
||||||
|
|
||||||
|
|
@ -10,19 +13,7 @@ struct cg_xdg_shell_view {
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
struct wl_listener unmap;
|
struct wl_listener unmap;
|
||||||
struct wl_listener map;
|
struct wl_listener map;
|
||||||
struct wl_listener commit;
|
|
||||||
struct wl_listener request_fullscreen;
|
struct wl_listener request_fullscreen;
|
||||||
struct wl_listener new_popup;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cg_xdg_popup {
|
|
||||||
struct cg_view_child view_child;
|
|
||||||
struct wlr_xdg_popup *wlr_popup;
|
|
||||||
|
|
||||||
struct wl_listener destroy;
|
|
||||||
struct wl_listener map;
|
|
||||||
struct wl_listener unmap;
|
|
||||||
struct wl_listener new_popup;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_xdg_decoration {
|
struct cg_xdg_decoration {
|
||||||
|
|
|
||||||
105
xwayland.c
105
xwayland.c
|
|
@ -1,11 +1,6 @@
|
||||||
/*
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
* Cagebreak: A Wayland tiling compositor.
|
// SPDX -License-Identifier: MIT
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2022 The Cagebreak Authors
|
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
|
||||||
*
|
|
||||||
* See the LICENSE file accompanying this file.
|
|
||||||
*/
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
|
|
@ -44,11 +39,11 @@ xwayland_view_should_manage(const struct cg_view *view) {
|
||||||
return !xwayland_surface->override_redirect;
|
return !xwayland_surface->override_redirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static pid_t
|
||||||
get_title(const struct cg_view *view) {
|
get_pid(const struct cg_view *view) {
|
||||||
const struct cg_xwayland_view *xwayland_view =
|
struct wlr_xwayland_surface *surf =
|
||||||
xwayland_view_from_const_view(view);
|
wlr_xwayland_surface_from_wlr_surface(view->wlr_surface);
|
||||||
return xwayland_view->xwayland_surface->title;
|
return surf->pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -64,6 +59,10 @@ static void
|
||||||
activate(struct cg_view *view, bool activate) {
|
activate(struct cg_view *view, bool activate) {
|
||||||
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
||||||
wlr_xwayland_surface_activate(xwayland_view->xwayland_surface, activate);
|
wlr_xwayland_surface_activate(xwayland_view->xwayland_surface, activate);
|
||||||
|
if(activate) {
|
||||||
|
wlr_xwayland_surface_restack(xwayland_view->xwayland_surface, NULL,
|
||||||
|
XCB_STACK_MODE_ABOVE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -75,35 +74,25 @@ close(struct cg_view *view) {
|
||||||
static void
|
static void
|
||||||
maximize(struct cg_view *view, int width, int height) {
|
maximize(struct cg_view *view, int width, int height) {
|
||||||
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
||||||
struct cg_output *output = view->workspace->output;
|
xcb_size_hints_t *hints = xwayland_view->xwayland_surface->size_hints;
|
||||||
|
|
||||||
struct wlr_box *box = wlr_output_layout_get_box(
|
|
||||||
view->server->output_layout, view->workspace->output->wlr_output);
|
|
||||||
struct wlr_xwayland_surface_size_hints *hints =
|
|
||||||
xwayland_view->xwayland_surface->size_hints;
|
|
||||||
|
|
||||||
if(hints != NULL && hints->flags & PMaxSize) {
|
if(hints != NULL && hints->flags & PMaxSize) {
|
||||||
if(width > hints->max_width) {
|
if(width > hints->max_width) {
|
||||||
wlr_output_damage_add_box(output->damage,
|
|
||||||
&(struct wlr_box){.x = view->ox + box->x,
|
|
||||||
.y = view->oy + box->y,
|
|
||||||
.width = width,
|
|
||||||
.height = height});
|
|
||||||
width = hints->max_width;
|
width = hints->max_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(height > hints->max_height) {
|
if(height > hints->max_height) {
|
||||||
wlr_output_damage_add_box(output->damage,
|
|
||||||
&(struct wlr_box){.x = view->ox + box->x,
|
|
||||||
.y = view->oy + box->y,
|
|
||||||
.width = width,
|
|
||||||
.height = height});
|
|
||||||
height = hints->max_height;
|
height = hints->max_height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_xwayland_surface_configure(xwayland_view->xwayland_surface, view->ox,
|
struct wlr_box output_box;
|
||||||
view->oy, width, height);
|
wlr_output_layout_get_box(view->server->output_layout,
|
||||||
|
view->server->curr_output->wlr_output,
|
||||||
|
&output_box);
|
||||||
|
wlr_xwayland_surface_configure(xwayland_view->xwayland_surface,
|
||||||
|
view->ox + output_box.x,
|
||||||
|
view->oy + output_box.y, width, height);
|
||||||
wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true);
|
wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,18 +102,6 @@ destroy(struct cg_view *view) {
|
||||||
free(xwayland_view);
|
free(xwayland_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator,
|
|
||||||
void *data) {
|
|
||||||
wlr_surface_for_each_surface(view->wlr_surface, iterator, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_surface *
|
|
||||||
wlr_surface_at(const struct cg_view *view, double sx, double sy, double *sub_x,
|
|
||||||
double *sub_y) {
|
|
||||||
return wlr_surface_surface_at(view->wlr_surface, sx, sy, sub_x, sub_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_xwayland_surface_request_fullscreen(struct wl_listener *listener,
|
handle_xwayland_surface_request_fullscreen(struct wl_listener *listener,
|
||||||
void *data) {
|
void *data) {
|
||||||
|
|
@ -136,33 +113,12 @@ handle_xwayland_surface_request_fullscreen(struct wl_listener *listener,
|
||||||
xwayland_surface->fullscreen);
|
xwayland_surface->fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
handle_xwayland_surface_commit(struct wl_listener *listener, void *_data) {
|
|
||||||
struct cg_xwayland_view *xwayland_view =
|
|
||||||
wl_container_of(listener, xwayland_view, commit);
|
|
||||||
struct cg_view *view = &xwayland_view->view;
|
|
||||||
/* xwayland surface has moved */
|
|
||||||
if(xwayland_view->xwayland_surface->x != view->ox ||
|
|
||||||
xwayland_view->xwayland_surface->y != view->oy) {
|
|
||||||
output_damage_surface(view->workspace->output, view->wlr_surface,
|
|
||||||
view->ox, view->oy, true);
|
|
||||||
view->ox = xwayland_view->xwayland_surface->x;
|
|
||||||
view->oy = xwayland_view->xwayland_surface->y;
|
|
||||||
output_damage_surface(view->workspace->output, view->wlr_surface,
|
|
||||||
view->ox, view->oy, true);
|
|
||||||
} else {
|
|
||||||
view_damage_part(view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_xwayland_surface_unmap(struct wl_listener *listener, void *_data) {
|
handle_xwayland_surface_unmap(struct wl_listener *listener, void *_data) {
|
||||||
struct cg_xwayland_view *xwayland_view =
|
struct cg_xwayland_view *xwayland_view =
|
||||||
wl_container_of(listener, xwayland_view, unmap);
|
wl_container_of(listener, xwayland_view, unmap);
|
||||||
struct cg_view *view = &xwayland_view->view;
|
struct cg_view *view = &xwayland_view->view;
|
||||||
|
|
||||||
wl_list_remove(&xwayland_view->commit.link);
|
|
||||||
|
|
||||||
view_unmap(view);
|
view_unmap(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,21 +127,18 @@ handle_xwayland_surface_map(struct wl_listener *listener, void *_data) {
|
||||||
struct cg_xwayland_view *xwayland_view =
|
struct cg_xwayland_view *xwayland_view =
|
||||||
wl_container_of(listener, xwayland_view, map);
|
wl_container_of(listener, xwayland_view, map);
|
||||||
struct cg_view *view = &xwayland_view->view;
|
struct cg_view *view = &xwayland_view->view;
|
||||||
|
|
||||||
if(!xwayland_view_should_manage(view)) {
|
if(!xwayland_view_should_manage(view)) {
|
||||||
view->ox = xwayland_view->xwayland_surface->x;
|
struct wlr_output_layout *output_layout = view->server->output_layout;
|
||||||
view->oy = xwayland_view->xwayland_surface->y;
|
struct wlr_box output_box;
|
||||||
|
wlr_output_layout_get_box(
|
||||||
|
output_layout, view->server->curr_output->wlr_output, &output_box);
|
||||||
|
view->ox = xwayland_view->xwayland_surface->x - output_box.x;
|
||||||
|
view->oy = xwayland_view->xwayland_surface->y - output_box.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
xwayland_view->commit.notify = handle_xwayland_surface_commit;
|
|
||||||
wl_signal_add(&xwayland_view->xwayland_surface->surface->events.commit,
|
|
||||||
&xwayland_view->commit);
|
|
||||||
|
|
||||||
view_map(view, xwayland_view->xwayland_surface->surface,
|
view_map(view, xwayland_view->xwayland_surface->surface,
|
||||||
view->server->curr_output
|
view->server->curr_output
|
||||||
->workspaces[view->server->curr_output->curr_workspace]);
|
->workspaces[view->server->curr_output->curr_workspace]);
|
||||||
|
|
||||||
view_damage_whole(view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -204,16 +157,12 @@ handle_xwayland_surface_destroy(struct wl_listener *listener, void *_data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct cg_view_impl xwayland_view_impl = {
|
static const struct cg_view_impl xwayland_view_impl = {
|
||||||
.get_title = get_title,
|
.get_pid = get_pid,
|
||||||
.is_primary = is_primary,
|
.is_primary = is_primary,
|
||||||
.activate = activate,
|
.activate = activate,
|
||||||
.close = close,
|
.close = close,
|
||||||
.maximize = maximize,
|
.maximize = maximize,
|
||||||
.destroy = destroy,
|
.destroy = destroy,
|
||||||
.for_each_surface = for_each_surface,
|
|
||||||
/* XWayland doesn't have a separate popup iterator. */
|
|
||||||
.for_each_popup = NULL,
|
|
||||||
.wlr_surface_at = wlr_surface_at,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2020 - 2023, project-repo and the cagebreak contributors
|
||||||
|
// SPDX -License-Identifier: MIT
|
||||||
|
|
||||||
#ifndef CG_XWAYLAND_H
|
#ifndef CG_XWAYLAND_H
|
||||||
#define CG_XWAYLAND_H
|
#define CG_XWAYLAND_H
|
||||||
|
|
||||||
|
|
@ -10,7 +13,6 @@ struct cg_xwayland_view {
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
struct wl_listener unmap;
|
struct wl_listener unmap;
|
||||||
struct wl_listener map;
|
struct wl_listener map;
|
||||||
struct wl_listener commit;
|
|
||||||
struct wl_listener request_fullscreen;
|
struct wl_listener request_fullscreen;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue