mirror of
https://cagebreak.project-repo.co/cagebreak.git
synced 2026-08-09 07:09:11 +02:00
Release 1.0.0
- Add basic tiling compositor functionality
This commit is contained in:
commit
de6c07ca2a
45 changed files with 6864 additions and 0 deletions
11
.clang-format
Normal file
11
.clang-format
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
BasedOnStyle: LLVM
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
IndentWidth: 4
|
||||
ObjCBlockIndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: ForIndentation
|
||||
AlwaysBreakAfterReturnType: TopLevel
|
||||
SpaceBeforeParens: Never
|
||||
...
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
build
|
||||
12
Bugs.md
Normal file
12
Bugs.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Bugs
|
||||
|
||||
This file is slightly unusual to exist given that the project is currently
|
||||
developed on github which offers an issue tracker. However, documenting
|
||||
bugs inside of the repository makes the project more robust, should it ever
|
||||
be moved to another hosting provider with a different issue tracking approach
|
||||
or in case of network outages, where the github issue tracker may be unavailable
|
||||
for developers. It also makes possible the description of bugs which were not
|
||||
submitted on the issue tracker, should any other forms of contact become
|
||||
available.
|
||||
|
||||
## Issues
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
Copyright (c) 2020 The Cagebreak authors
|
||||
Copyright (c) 2018-2020 Jente Hidskes
|
||||
Copyright (c) 2019 The Sway 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.
|
||||
155
README.md
Normal file
155
README.md
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
# Cagebreak: A Wayland Tiling Compositor Inspired by Ratpoison
|
||||
|
||||
This is Cagebreak, a Wayland tiling compositor. The goal of this project is to
|
||||
provide a successor to ratpoison for wayland users. However, this is
|
||||
no reimplementation of ratpoison. Should you like to know if a feature
|
||||
will be implemented, open an issue or get in touch with the development team.
|
||||
|
||||
This README is only relevant for development resources and instructions. For a
|
||||
description of Cagebreak and installation instructions for end-users, please see
|
||||
the man page supplied in the `man/` directory and the [Wiki](https://github.com/project-repo/cagebreak/wiki/).
|
||||
|
||||
Cagebreak is based on [Cage](https://github.com/Hjdskes/cage), a Wayland kiosk
|
||||
compositor.
|
||||
|
||||
## Experimenting with Cagebreak
|
||||
|
||||
Cagebreak is currently being 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.
|
||||
|
||||
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
|
||||
developed against the latest tag of wlroots, in order not to constantly chase
|
||||
breaking changes as soon as they occur.
|
||||
|
||||
Simply execute the following steps to build Cagebreak:
|
||||
|
||||
```
|
||||
$ meson build
|
||||
$ ninja -C build
|
||||
```
|
||||
|
||||
### Release Build
|
||||
|
||||
By default, this builds a debug build. To build a release build, use `meson
|
||||
build --buildtype=release`.
|
||||
|
||||
### Xwayland Support
|
||||
|
||||
Cagebreak comes with compile-time support for XWayland. To enable this,
|
||||
first make sure that your version of wlroots is compiled with this
|
||||
option. Then, add `-Dxwayland=true` to the `meson` command above. Note
|
||||
that you'll need to have the XWayland binary installed on your system
|
||||
for this to work.
|
||||
|
||||
### Running Cagebreak
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
## Contributing to Cagebreak
|
||||
|
||||
Cagebreak is currently developed to fit the needs of its creators. Should you 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 or any other
|
||||
part of this file. Do what you can, open an issue and we will collaborate
|
||||
toward a solution.
|
||||
|
||||
### Branching strategy
|
||||
|
||||
All features are to be developed on feature branches, named after the feature.
|
||||
|
||||
There exists a branch `development` to which all reasonable feature branches
|
||||
are merged for final testing.
|
||||
|
||||
Once `development` is ready for a release, meaning that the release checklist is fulfilled,
|
||||
it is merged into `master`, creating a new release, which is tagged and signed.
|
||||
|
||||
Merging into master is always done by
|
||||
|
||||
```
|
||||
git merge --no-ff development
|
||||
git tag -u keyid version
|
||||
git push origin master
|
||||
```
|
||||
|
||||
and a log message roughly describing the features is added in the commit.
|
||||
|
||||
### Releases
|
||||
|
||||
Release checklist
|
||||
|
||||
* [ ] Cursory testing
|
||||
* [ ] libfuzzer testing
|
||||
* [ ] Version Number
|
||||
* [ ] -v flag
|
||||
* [ ] git tag
|
||||
* [ ] Relevant Documentation
|
||||
* [ ] New features documented
|
||||
* [ ] man page
|
||||
* [ ] wiki
|
||||
* [ ] New configuration documented
|
||||
* [ ] man page
|
||||
* [ ] wiki
|
||||
* [ ] Changelog in README
|
||||
* [ ] Document fixed bugs in Bugs.md
|
||||
* [ ] Signature
|
||||
* [ ] Branching Strategy
|
||||
|
||||
### Signing Key
|
||||
|
||||
All releases are signed by at least one of the following collection of
|
||||
keys.
|
||||
|
||||
* E79F6D9E113529F4B1FFE4D5C4F974D70CEC2C5B
|
||||
* 4739D329C9187A1C2795C20A02ABFDEC3A40545F
|
||||
|
||||
Should we at any point retire a key, we will only replace it with keys signed
|
||||
by at least one of the above collection.
|
||||
|
||||
Should we at any point have official mail addresses, their keys will be signed by
|
||||
a valid key noted above.
|
||||
|
||||
The full public keys can be found in `keys/` along with any revocation certificates.
|
||||
|
||||
### Reproducible Builds
|
||||
|
||||
Currently our project seems to build the same way on any given system, when compiled
|
||||
multiple times. However, at the moment we are unable to supply instructions
|
||||
for building our software reproducibly. Reproducible builds are planned for the
|
||||
near future.
|
||||
|
||||
### Fuzzing
|
||||
|
||||
Along with the project source code, a fuzzing framework based on `libfuzzer` is
|
||||
supplied. This allows for the testing of the parsing code responsible for reading
|
||||
the `cagebreak` configuration file. When the `libfuzzer` is available (please
|
||||
use the `clang` compiler to enable it), building the fuzz-testing software can
|
||||
be enabled by passing `-Dfuzz=true` to meson. This generates a `build/fuzz/fuzz-parse`
|
||||
binary according to the `libfuzzer` specifications. Further documentation on
|
||||
how to run this binary can be found [here](https://llvm.org/docs/LibFuzzer.html).
|
||||
|
||||
## Bugs
|
||||
|
||||
For any bug, please [create an
|
||||
issue](https://github.com/project-repo/cagebreak/issues/new) on
|
||||
[GitHub](https://github.com/project-rep/cagebreak).
|
||||
|
||||
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.
|
||||
|
||||
## Changelog
|
||||
|
||||
### Release 1.0.0
|
||||
|
||||
Adds basic tiling window manager functionality to cage.
|
||||
|
||||
## License
|
||||
|
||||
Please see [LICENSE](https://github.com/project-repo/cagebreak/blob/master/LICENSE)
|
||||
537
cagebreak.c
Normal file
537
cagebreak.c
Normal file
|
|
@ -0,0 +1,537 @@
|
|||
/*
|
||||
* Cagebreak: A Wayland tiling compositor.
|
||||
*
|
||||
* Copyright (C) 2018-2020 Jente Hidskes
|
||||
*
|
||||
* See the LICENSE file accompanying this file.
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200812L
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_compositor.h>
|
||||
#include <wlr/types/wlr_cursor.h>
|
||||
#include <wlr/types/wlr_data_device.h>
|
||||
#include <wlr/types/wlr_export_dmabuf_v1.h>
|
||||
#include <wlr/types/wlr_gamma_control_v1.h>
|
||||
#include <wlr/types/wlr_idle.h>
|
||||
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
||||
#include <wlr/types/wlr_output_damage.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_screencopy_v1.h>
|
||||
#include <wlr/types/wlr_server_decoration.h>
|
||||
#if CG_HAS_XWAYLAND
|
||||
#include <wlr/types/wlr_xcursor_manager.h>
|
||||
#endif
|
||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||
#include <wlr/types/wlr_xdg_output_v1.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/util/log.h>
|
||||
#if CG_HAS_XWAYLAND
|
||||
#include <wlr/xwayland.h>
|
||||
#endif
|
||||
|
||||
#include "idle_inhibit_v1.h"
|
||||
#include "keybinding.h"
|
||||
#include "message.h"
|
||||
#include "output.h"
|
||||
#include "parse.h"
|
||||
#include "seat.h"
|
||||
#include "server.h"
|
||||
#include "view.h"
|
||||
#include "xdg_shell.h"
|
||||
#if CG_HAS_XWAYLAND
|
||||
#include "xwayland.h"
|
||||
#endif
|
||||
|
||||
#ifndef WAIT_ANY
|
||||
#define WAIT_ANY -1
|
||||
#endif
|
||||
|
||||
void
|
||||
set_sig_handler(int sig, void (*action)(int)) {
|
||||
struct sigaction act;
|
||||
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = action;
|
||||
sigemptyset(&act.sa_mask);
|
||||
if(sigaction(sig, &act, NULL)) {
|
||||
wlr_log(WLR_ERROR, "Error setting signal handler: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sig_chld_handler(int signal) {
|
||||
int pid = 1;
|
||||
while(pid > 0) {
|
||||
pid = waitpid(WAIT_ANY, NULL, WNOHANG);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
drop_permissions(void) {
|
||||
if(getuid() != geteuid() || getgid() != getegid()) {
|
||||
if(setuid(getuid()) != 0 || setgid(getgid()) != 0) {
|
||||
wlr_log(WLR_ERROR, "Unable to drop root, refusing to start");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(setuid(0) != -1) {
|
||||
wlr_log(WLR_ERROR, "Unable to drop root (we shouldn't be able to "
|
||||
"restore it after setuid), refusing to start");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_signal(int signal, void *const data) {
|
||||
struct cg_server *server = data;
|
||||
|
||||
switch(signal) {
|
||||
case SIGINT:
|
||||
/* Fallthrough */
|
||||
case SIGTERM:
|
||||
display_terminate(server);
|
||||
return 0;
|
||||
case SIGALRM: {
|
||||
struct cg_output *output;
|
||||
wl_list_for_each(output, &server->outputs, link) {
|
||||
message_clear(output);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
usage(FILE *file, const char *const cage) {
|
||||
fprintf(file,
|
||||
"Usage: %s [OPTIONS]\n"
|
||||
"\n"
|
||||
" -r\t Rotate the output 90 degrees clockwise, specify up to three "
|
||||
"times\n"
|
||||
#ifdef DEBUG
|
||||
" -D\t Turn on damage tracking debugging\n"
|
||||
#endif
|
||||
" -h\t Display this help message\n"
|
||||
" -v\t Show the version number and exit\n",
|
||||
cage);
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_args(struct cg_server *server, int argc, char *argv[]) {
|
||||
int c;
|
||||
#ifdef DEBUG
|
||||
while((c = getopt(argc, argv, "rDhv")) != -1) {
|
||||
#else
|
||||
while((c = getopt(argc, argv, "rhv")) != -1) {
|
||||
#endif
|
||||
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':
|
||||
usage(stdout, argv[0]);
|
||||
return false;
|
||||
case 'v':
|
||||
fprintf(stdout, "Cagebreak version " CG_VERSION "\n");
|
||||
exit(0);
|
||||
default:
|
||||
usage(stderr, argv[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(optind > argc) {
|
||||
usage(stderr, argv[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Parse config file. Lines longer than "max_line_size" are ignored */
|
||||
int
|
||||
set_configuration(struct cg_server *server,
|
||||
const char *const config_file_path) {
|
||||
FILE *config_file = fopen(config_file_path, "r");
|
||||
if(config_file == NULL) {
|
||||
wlr_log(WLR_ERROR, "Could not open config file \"%s\"",
|
||||
config_file_path);
|
||||
return -1;
|
||||
}
|
||||
size_t max_line_size = 256;
|
||||
char line[max_line_size * sizeof(char)];
|
||||
for(unsigned int line_num = 1;
|
||||
fgets(line, max_line_size, config_file) != NULL; ++line_num) {
|
||||
line[strcspn(line, "\n")] = '\0';
|
||||
if(*line != '\0' && *line != '#') {
|
||||
if(parse_rc_line(server, line) != 0) {
|
||||
wlr_log(WLR_ERROR, "Error in config file \"%s\", line %d\n",
|
||||
config_file_path, line_num);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
get_config_file() {
|
||||
const char *config_home_path = getenv("XDG_CONFIG_HOME");
|
||||
char *addition = "/cagebreak/config";
|
||||
if(config_home_path == NULL || config_home_path[0] == '\0') {
|
||||
config_home_path = getenv("HOME");
|
||||
if(config_home_path == NULL || config_home_path[0] == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
addition = "/.config/cagebreak/config";
|
||||
}
|
||||
char *config_path = malloc(
|
||||
(strlen(config_home_path) + strlen(addition) + 1) * sizeof(char));
|
||||
sprintf(config_path, "%s%s", config_home_path, addition);
|
||||
return config_path;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
struct cg_server server = {0};
|
||||
struct wl_event_loop *event_loop = NULL;
|
||||
struct wl_event_source *sigint_source = NULL;
|
||||
struct wl_event_source *sigterm_source = NULL;
|
||||
struct wl_event_source *sigalrm_source = NULL;
|
||||
struct wlr_backend *backend = NULL;
|
||||
struct wlr_renderer *renderer = NULL;
|
||||
struct wlr_compositor *compositor = NULL;
|
||||
struct wlr_data_device_manager *data_device_manager = NULL;
|
||||
struct wlr_server_decoration_manager *server_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_screencopy_manager_v1 *screencopy_manager = NULL;
|
||||
struct wlr_xdg_output_manager_v1 *output_manager = NULL;
|
||||
struct wlr_gamma_control_manager_v1 *gamma_control_manager = NULL;
|
||||
struct wlr_xdg_shell *xdg_shell = NULL;
|
||||
#if CG_HAS_XWAYLAND
|
||||
struct wlr_xwayland *xwayland = NULL;
|
||||
struct wlr_xcursor_manager *xcursor_manager = NULL;
|
||||
#endif
|
||||
int ret = 0;
|
||||
|
||||
if(!parse_args(&server, argc, argv)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
wlr_log_init(WLR_DEBUG, NULL);
|
||||
#else
|
||||
wlr_log_init(WLR_ERROR, NULL);
|
||||
#endif
|
||||
|
||||
/* Wayland requires XDG_RUNTIME_DIR to be set. */
|
||||
if(!getenv("XDG_RUNTIME_DIR")) {
|
||||
wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is not set in the environment");
|
||||
return 1;
|
||||
}
|
||||
|
||||
server.wl_display = wl_display_create();
|
||||
if(!server.wl_display) {
|
||||
wlr_log(WLR_ERROR, "Cannot allocate a Wayland display");
|
||||
return 1;
|
||||
}
|
||||
|
||||
server.running = true;
|
||||
|
||||
server.modes = malloc(4 * sizeof(char *));
|
||||
server.modes[0] = strdup("top");
|
||||
server.modes[1] = strdup("root");
|
||||
server.modes[2] = strdup("resize");
|
||||
server.modes[3] = NULL;
|
||||
|
||||
server.nws = 1;
|
||||
server.message_timeout = 2;
|
||||
|
||||
event_loop = wl_display_get_event_loop(server.wl_display);
|
||||
sigint_source =
|
||||
wl_event_loop_add_signal(event_loop, SIGINT, handle_signal, &server);
|
||||
sigterm_source =
|
||||
wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server);
|
||||
sigalrm_source =
|
||||
wl_event_loop_add_signal(event_loop, SIGALRM, handle_signal, &server);
|
||||
server.event_loop = event_loop;
|
||||
|
||||
set_sig_handler(SIGCHLD, sig_chld_handler);
|
||||
|
||||
backend = wlr_backend_autocreate(server.wl_display, NULL);
|
||||
if(!backend) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the wlroots backend");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
server.backend = backend;
|
||||
|
||||
if(!drop_permissions()) {
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
server.keybindings = keybinding_list_init();
|
||||
if(server.keybindings == NULL || server.keybindings->keybindings == NULL) {
|
||||
wlr_log(WLR_ERROR, "Unable to allocate keybindings");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
renderer = wlr_backend_get_renderer(backend);
|
||||
wlr_renderer_init_wl_display(renderer, server.wl_display);
|
||||
|
||||
server.bg_color = (float[4]){0, 0, 0, 1};
|
||||
wl_list_init(&server.outputs);
|
||||
|
||||
server.output_layout = wlr_output_layout_create();
|
||||
if(!server.output_layout) {
|
||||
wlr_log(WLR_ERROR, "Unable to create output layout");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
compositor = wlr_compositor_create(server.wl_display, 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);
|
||||
if(!data_device_manager) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the data device manager");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Configure a listener to be notified when new outputs are
|
||||
* available on the backend. We use this only to detect the
|
||||
* first output and ignore subsequent outputs. */
|
||||
server.new_output.notify = handle_new_output;
|
||||
wl_signal_add(&backend->events.new_output, &server.new_output);
|
||||
|
||||
server.seat = seat_create(&server, backend);
|
||||
if(!server.seat) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the seat");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
server.idle = wlr_idle_create(server.wl_display);
|
||||
if(!server.idle) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the idle tracker");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
server.idle_inhibit_v1 = wlr_idle_inhibit_v1_create(server.wl_display);
|
||||
if(!server.idle_inhibit_v1) {
|
||||
wlr_log(WLR_ERROR, "Cannot create the idle inhibitor");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
server.new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1_new;
|
||||
wl_signal_add(&server.idle_inhibit_v1->events.new_inhibitor,
|
||||
&server.new_idle_inhibitor_v1);
|
||||
wl_list_init(&server.inhibitors);
|
||||
|
||||
xdg_shell = wlr_xdg_shell_create(server.wl_display);
|
||||
if(!xdg_shell) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the XDG shell interface");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
server.new_xdg_shell_surface.notify = handle_xdg_shell_surface_new;
|
||||
wl_signal_add(&xdg_shell->events.new_surface,
|
||||
&server.new_xdg_shell_surface);
|
||||
|
||||
xdg_decoration_manager =
|
||||
wlr_xdg_decoration_manager_v1_create(server.wl_display);
|
||||
if(!xdg_decoration_manager) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the XDG decoration manager");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
wl_signal_add(&xdg_decoration_manager->events.new_toplevel_decoration,
|
||||
&server.xdg_toplevel_decoration);
|
||||
server.xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration;
|
||||
|
||||
server_decoration_manager =
|
||||
wlr_server_decoration_manager_create(server.wl_display);
|
||||
if(!server_decoration_manager) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the server decoration manager");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
wlr_server_decoration_manager_set_default_mode(
|
||||
server_decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
|
||||
|
||||
export_dmabuf_manager =
|
||||
wlr_export_dmabuf_manager_v1_create(server.wl_display);
|
||||
if(!export_dmabuf_manager) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the export DMABUF manager");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
screencopy_manager = wlr_screencopy_manager_v1_create(server.wl_display);
|
||||
if(!screencopy_manager) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the screencopy manager");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
output_manager = wlr_xdg_output_manager_v1_create(server.wl_display,
|
||||
server.output_layout);
|
||||
if(!output_manager) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the output manager");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
gamma_control_manager =
|
||||
wlr_gamma_control_manager_v1_create(server.wl_display);
|
||||
if(!gamma_control_manager) {
|
||||
wlr_log(WLR_ERROR, "Unable to create the gamma control manager");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
#if CG_HAS_XWAYLAND
|
||||
xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
|
||||
if(!xwayland) {
|
||||
wlr_log(WLR_ERROR, "Cannot create XWayland server");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
server.new_xwayland_surface.notify = handle_xwayland_surface_new;
|
||||
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) {
|
||||
wlr_log_errno(WLR_ERROR, "Unable to set DISPLAY for XWayland.",
|
||||
"Clients may not be able to connect");
|
||||
} else {
|
||||
wlr_log(WLR_DEBUG, "XWayland is running on display %s",
|
||||
xwayland->display_name);
|
||||
}
|
||||
|
||||
if(wlr_xcursor_manager_load(xcursor_manager, 1)) {
|
||||
wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme");
|
||||
}
|
||||
struct wlr_xcursor *xcursor =
|
||||
wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1);
|
||||
if(xcursor) {
|
||||
struct wlr_xcursor_image *image = xcursor->images[0];
|
||||
wlr_xwayland_set_cursor(xwayland, image->buffer, image->width * 4,
|
||||
image->width, image->height, image->hotspot_x,
|
||||
image->hotspot_y);
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *socket = wl_display_add_socket_auto(server.wl_display);
|
||||
if(!socket) {
|
||||
wlr_log_errno(WLR_ERROR, "Unable to open Wayland socket");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if(!wlr_backend_start(backend)) {
|
||||
wlr_log(WLR_ERROR, "Unable to start the wlroots backend");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if(setenv("WAYLAND_DISPLAY", socket, true) < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.",
|
||||
"Clients may not be able to connect");
|
||||
} else {
|
||||
wlr_log(WLR_DEBUG,
|
||||
"Cagebreak " CG_VERSION " is running on Wayland display %s",
|
||||
socket);
|
||||
}
|
||||
|
||||
#if CG_HAS_XWAYLAND
|
||||
wlr_xwayland_set_seat(xwayland, server.seat->seat);
|
||||
#endif
|
||||
|
||||
{ // config_file should only be visible as long as it is valid
|
||||
char *config_file = get_config_file();
|
||||
if(config_file == NULL) {
|
||||
wlr_log(WLR_ERROR, "Unable to get path to config file");
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
if(set_configuration(&server, config_file) != 0) {
|
||||
free(config_file);
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
free(config_file);
|
||||
}
|
||||
|
||||
/* Place the cursor to the topl left of the output layout. */
|
||||
wlr_cursor_warp(server.seat->cursor, NULL, 0, 0);
|
||||
|
||||
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);
|
||||
|
||||
end:
|
||||
for(unsigned int i = 0; server.modes[i] != NULL; ++i) {
|
||||
free(server.modes[i]);
|
||||
}
|
||||
free(server.modes);
|
||||
|
||||
keybinding_list_free(server.keybindings);
|
||||
wl_event_source_remove(sigint_source);
|
||||
wl_event_source_remove(sigterm_source);
|
||||
wl_event_source_remove(sigalrm_source);
|
||||
|
||||
seat_destroy(server.seat);
|
||||
/* This function is not null-safe, but we only ever get here
|
||||
with a proper wl_display. */
|
||||
wl_display_destroy(server.wl_display);
|
||||
wlr_output_layout_destroy(server.output_layout);
|
||||
return ret;
|
||||
}
|
||||
8
config.h.in
Normal file
8
config.h.in
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef CG_CONFIG_H
|
||||
#define CG_CONFIG_H
|
||||
|
||||
#mesondefine CG_HAS_XWAYLAND
|
||||
|
||||
#mesondefine CG_VERSION
|
||||
|
||||
#endif
|
||||
72
examples/config
Normal file
72
examples/config
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
######################
|
||||
# General settings an key bindings
|
||||
######################
|
||||
|
||||
exec xterm
|
||||
|
||||
workspaces 6
|
||||
background 0.25 0.21 0.2
|
||||
|
||||
escape C-t
|
||||
|
||||
bind s hsplit
|
||||
bind S vsplit
|
||||
bind Q only
|
||||
bind D quit
|
||||
bind Tab focus
|
||||
bind A-Tab focusprev
|
||||
bind n next
|
||||
bind p prev
|
||||
bind w exec firefox
|
||||
bind R setmode resize
|
||||
bind N nextscreen
|
||||
bind P prevscreen
|
||||
bind a time
|
||||
bind C-n movetonextscreen
|
||||
bind H exchangeleft
|
||||
bind J exchangedown
|
||||
bind K exchangeup
|
||||
bind L exchangeright
|
||||
bind h focusleft
|
||||
bind j focusdown
|
||||
bind k focusup
|
||||
bind l focusright
|
||||
bind t exec xterm
|
||||
definekey resize h resizeleft
|
||||
definekey resize l resizeright
|
||||
definekey resize j resizedown
|
||||
definekey resize k resizeup
|
||||
definekey resize Escape setmode top
|
||||
|
||||
######################
|
||||
#Workspaces
|
||||
######################
|
||||
definekey top A-1 workspace 1
|
||||
definekey top A-2 workspace 2
|
||||
definekey top A-3 workspace 3
|
||||
definekey top A-4 workspace 4
|
||||
definekey top A-5 workspace 5
|
||||
definekey top A-6 workspace 6
|
||||
|
||||
definekey top C-1 movetoworkspace 1
|
||||
definekey top C-2 movetoworkspace 2
|
||||
definekey top C-3 movetoworkspace 3
|
||||
definekey top C-4 movetoworkspace 4
|
||||
definekey top C-5 movetoworkspace 5
|
||||
definekey top C-6 movetoworkspace 6
|
||||
|
||||
definekey top XF86_Switch_VT_1 switchvt 1
|
||||
definekey top XF86_Switch_VT_2 switchvt 2
|
||||
definekey top XF86_Switch_VT_3 switchvt 3
|
||||
definekey top XF86_Switch_VT_4 switchvt 4
|
||||
definekey top XF86_Switch_VT_5 switchvt 5
|
||||
definekey top XF86_Switch_VT_6 switchvt 6
|
||||
|
||||
######################
|
||||
#Bind Function keys
|
||||
######################
|
||||
definekey top XF86AudioMute exec pactl set-sink-mute 0 toggle
|
||||
definekey top XF86AudioLowerVolume exec pactl set-sink-mute 0 off&&amixer set Master 1%-
|
||||
definekey top XF86AudioRaiseVolume exec pactl set-sink-mute 0 off&&amixer set Master 1%+
|
||||
definekey top XF86MonBrightnessDown exec xbacklight -dec 1
|
||||
definekey top XF86MonBrightnessUp exec xbacklight -inc 1
|
||||
11
fuzz/execl_override.c
Normal file
11
fuzz/execl_override.c
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* This file is used by the fuzzer in order to prevent executing shell commands.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <time.h>
|
||||
|
||||
struct tm *(*orig_localtime)(const time_t *timep);
|
||||
|
||||
int
|
||||
execl(const char *pathname, const char *arg, ...) {
|
||||
return 0;
|
||||
}
|
||||
86
fuzz/fuzz-parse.c
Normal file
86
fuzz/fuzz-parse.c
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#define _POSIX_C_SOURCE 200812L
|
||||
#define FUZZING
|
||||
|
||||
#include "../keybinding.h"
|
||||
#include "../parse.h"
|
||||
#include "../seat.h"
|
||||
#include "../server.h"
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#ifndef WAIT_ANY
|
||||
#define WAIT_ANY -1
|
||||
#endif
|
||||
|
||||
void
|
||||
set_sig_handler(int sig) {
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_IGN; // handle signal by ignoring
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
if(sigaction(SIGCHLD, &sa, 0) == -1) {
|
||||
perror(0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
struct cg_server server;
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||
set_sig_handler(SIGCHLD);
|
||||
|
||||
server.wl_display = NULL;
|
||||
server.event_loop = NULL;
|
||||
|
||||
server.seat = malloc(sizeof(struct cg_seat));
|
||||
server.seat->mode = 0;
|
||||
server.seat->default_mode = 0;
|
||||
|
||||
server.idle = NULL;
|
||||
server.idle_inhibit_v1 = NULL;
|
||||
/* new_idle_inhibitor_v1 */
|
||||
wl_list_init(&server.inhibitors);
|
||||
|
||||
server.output_layout = NULL;
|
||||
wl_list_init(&server.outputs);
|
||||
server.curr_output = NULL;
|
||||
|
||||
/* new_output */
|
||||
|
||||
/* xdg_toplevel_decoration */
|
||||
/* new_xdg_shell_surface */
|
||||
/* new_xwayland_surface */
|
||||
server.keybindings = keybinding_list_init();
|
||||
server.output_transform = 0;
|
||||
|
||||
server.running = true;
|
||||
server.modes = malloc(sizeof(char *));
|
||||
server.modes[0] = NULL;
|
||||
server.nws = 1;
|
||||
server.message_timeout = 0;
|
||||
server.bg_color = malloc(4 * sizeof(float));
|
||||
server.bg_color[0] = 0;
|
||||
server.bg_color[1] = 0;
|
||||
server.bg_color[2] = 0;
|
||||
server.bg_color[3] = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
char *str = malloc(size * sizeof(char) + 1);
|
||||
strncpy(str, (char *)data, size);
|
||||
str[size] = 0;
|
||||
parse_rc_line(&server, str);
|
||||
free(str);
|
||||
keybinding_list_free(server.keybindings);
|
||||
server.keybindings = keybinding_list_init();
|
||||
return 0;
|
||||
}
|
||||
32
fuzz/meson.build
Normal file
32
fuzz/meson.build
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
fuzz_sources = [
|
||||
'fuzz-parse.c',
|
||||
]
|
||||
|
||||
fuzz_headers = [
|
||||
'../parse.h',
|
||||
]
|
||||
|
||||
inc = include_directories(['..','../build/'])
|
||||
|
||||
fuzz_dependencies = cagebreak_dependencies
|
||||
if fuzzing_engine.found()
|
||||
fuzz_dependencies += fuzzing_engine
|
||||
else
|
||||
link_args = [ '-fsanitize=fuzzer', ]
|
||||
endif
|
||||
|
||||
override_lib = shared_library('execl_override',
|
||||
[ 'execl_override.c' ],
|
||||
install: false
|
||||
)
|
||||
|
||||
executable(
|
||||
'fuzz-parse',
|
||||
fuzz_sources + fuzz_headers + cagebreak_headers + cagebreak_sources,
|
||||
dependencies: fuzz_dependencies,
|
||||
install: false,
|
||||
include_directories: inc,
|
||||
link_args: link_args + fuzz_link_args,
|
||||
c_args: fuzz_compile_args,
|
||||
link_with: override_lib,
|
||||
)
|
||||
65
idle_inhibit_v1.c
Normal file
65
idle_inhibit_v1.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Cagebreak: A Wayland tiling compositor.
|
||||
*
|
||||
* Copyright (C) 2020 The Cagebreak Authors
|
||||
* Copyright (C) 2018-2019 Jente Hidskes
|
||||
*
|
||||
* See the LICENSE file accompanying this file.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_idle.h>
|
||||
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
||||
|
||||
#include "idle_inhibit_v1.h"
|
||||
#include "server.h"
|
||||
|
||||
struct cg_idle_inhibitor_v1 {
|
||||
struct cg_server *server;
|
||||
|
||||
struct wl_list link; // server::inhibitors
|
||||
struct wl_listener destroy;
|
||||
};
|
||||
|
||||
static void
|
||||
idle_inhibit_v1_check_active(struct cg_server *server) {
|
||||
/* As of right now, this does not check whether the inhibitor
|
||||
* is visible or not.*/
|
||||
bool inhibited = !wl_list_empty(&server->inhibitors);
|
||||
wlr_idle_set_enabled(server->idle, NULL, !inhibited);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_destroy(struct wl_listener *listener, void *data) {
|
||||
struct cg_idle_inhibitor_v1 *inhibitor =
|
||||
wl_container_of(listener, inhibitor, destroy);
|
||||
struct cg_server *server = inhibitor->server;
|
||||
|
||||
wl_list_remove(&inhibitor->link);
|
||||
wl_list_remove(&inhibitor->destroy.link);
|
||||
free(inhibitor);
|
||||
|
||||
idle_inhibit_v1_check_active(server);
|
||||
}
|
||||
|
||||
void
|
||||
handle_idle_inhibitor_v1_new(struct wl_listener *listener, void *data) {
|
||||
struct cg_server *server =
|
||||
wl_container_of(listener, server, new_idle_inhibitor_v1);
|
||||
struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data;
|
||||
|
||||
struct cg_idle_inhibitor_v1 *inhibitor =
|
||||
calloc(1, sizeof(struct cg_idle_inhibitor_v1));
|
||||
if(!inhibitor) {
|
||||
return;
|
||||
}
|
||||
|
||||
inhibitor->server = server;
|
||||
wl_list_insert(&server->inhibitors, &inhibitor->link);
|
||||
|
||||
inhibitor->destroy.notify = handle_destroy;
|
||||
wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy);
|
||||
|
||||
idle_inhibit_v1_check_active(server);
|
||||
}
|
||||
9
idle_inhibit_v1.h
Normal file
9
idle_inhibit_v1.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef CG_IDLE_INHIBIT_H
|
||||
#define CG_IDLE_INHIBIT_H
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
void
|
||||
handle_idle_inhibitor_v1_new(struct wl_listener *listener, void *data);
|
||||
|
||||
#endif
|
||||
818
keybinding.c
Normal file
818
keybinding.c
Normal file
|
|
@ -0,0 +1,818 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_output_damage.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/backend/multi.h>
|
||||
|
||||
#include "keybinding.h"
|
||||
#include "message.h"
|
||||
#include "output.h"
|
||||
#include "seat.h"
|
||||
#include "server.h"
|
||||
#include "view.h"
|
||||
#include "workspace.h"
|
||||
#include "xdg_shell.h"
|
||||
|
||||
int
|
||||
keybinding_resize(struct keybinding_list *list) {
|
||||
if(list->length == list->capacity) {
|
||||
list->capacity *= 2;
|
||||
struct keybinding **new_list =
|
||||
realloc(list->keybindings, sizeof(void *) * list->capacity);
|
||||
if(new_list == NULL) {
|
||||
return -1;
|
||||
} else {
|
||||
list->keybindings = new_list;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct keybinding **
|
||||
find_keybinding(const struct keybinding_list *list,
|
||||
const struct keybinding *keybinding) {
|
||||
struct keybinding **it = list->keybindings;
|
||||
for(size_t i = 0; i < list->length; ++i, ++it) {
|
||||
if(!(keybinding->modifiers ^ (*it)->modifiers) &&
|
||||
keybinding->mode == (*it)->mode && keybinding->key == (*it)->key) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
keybinding_free(struct keybinding *keybinding) {
|
||||
switch(keybinding->action) {
|
||||
case KEYBINDING_RUN_COMMAND:
|
||||
if(keybinding->data.c != NULL) {
|
||||
free(keybinding->data.c);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
free(keybinding);
|
||||
}
|
||||
|
||||
int
|
||||
keybinding_list_push(struct keybinding_list *list,
|
||||
struct keybinding *keybinding) {
|
||||
|
||||
/* Error resizing list */
|
||||
if(keybinding_resize(list) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*Maintain that only a single keybinding for a key, modifier and mode may
|
||||
* exist*/
|
||||
struct keybinding **found_keybinding = find_keybinding(list, keybinding);
|
||||
if(found_keybinding != NULL) {
|
||||
keybinding_free(*found_keybinding);
|
||||
*found_keybinding = keybinding;
|
||||
wlr_log(WLR_DEBUG, "A keybinding was found twice in the config file.");
|
||||
} else {
|
||||
list->keybindings[list->length] = keybinding;
|
||||
++list->length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct keybinding_list *
|
||||
keybinding_list_init() {
|
||||
struct keybinding_list *list = malloc(sizeof(struct keybinding_list));
|
||||
list->keybindings = malloc(sizeof(struct keybinding *));
|
||||
list->capacity = 1;
|
||||
list->length = 0;
|
||||
return list;
|
||||
}
|
||||
|
||||
void
|
||||
keybinding_list_free(struct keybinding_list *list) {
|
||||
for(unsigned int i = 0; i < list->length; ++i) {
|
||||
keybinding_free(list->keybindings[i]);
|
||||
}
|
||||
free(list->keybindings);
|
||||
free(list);
|
||||
}
|
||||
|
||||
struct cg_tile *
|
||||
find_right_tile(const struct cg_tile *tile) {
|
||||
struct cg_tile *it = tile->next;
|
||||
while(it != tile && it->tile.x != tile->tile.x + tile->tile.width) {
|
||||
it = it->next;
|
||||
}
|
||||
if(it == tile) {
|
||||
return NULL;
|
||||
} else {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
struct cg_tile *
|
||||
find_left_tile(const struct cg_tile *tile) {
|
||||
struct cg_tile *it = tile->prev;
|
||||
while(it != tile && it->tile.x + it->tile.width != tile->tile.x) {
|
||||
it = it->prev;
|
||||
}
|
||||
if(it == tile) {
|
||||
return NULL;
|
||||
} else {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
struct cg_tile *
|
||||
find_top_tile(const struct cg_tile *tile) {
|
||||
struct cg_tile *it = tile->prev;
|
||||
while(it != tile && it->tile.y + it->tile.height != tile->tile.y) {
|
||||
it = it->prev;
|
||||
}
|
||||
if(it == tile) {
|
||||
return NULL;
|
||||
} else {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
struct cg_tile *
|
||||
find_bottom_tile(const struct cg_tile *tile) {
|
||||
struct cg_tile *it = tile->next;
|
||||
while(it != tile && it->tile.y != tile->tile.y + tile->tile.height) {
|
||||
it = it->next;
|
||||
}
|
||||
if(it == tile) {
|
||||
return NULL;
|
||||
} else {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
swap_tile(struct cg_tile *tile,
|
||||
struct cg_tile *(*find_tile)(const struct cg_tile *)) {
|
||||
struct cg_tile *swap_tile = find_tile(tile);
|
||||
struct cg_server *server = tile->workspace->server;
|
||||
if(swap_tile == NULL || swap_tile == tile) {
|
||||
return;
|
||||
}
|
||||
struct cg_view *tmp_view = tile->view;
|
||||
tile->view = swap_tile->view;
|
||||
swap_tile->view = tmp_view;
|
||||
if(tile->view != NULL) {
|
||||
view_maximize(tile->view, &tile->tile);
|
||||
view_damage_whole(tile->view);
|
||||
} else {
|
||||
wlr_output_damage_add_box(tile->workspace->output->damage, &tile->tile);
|
||||
}
|
||||
workspace_focus_tile(
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace],
|
||||
swap_tile);
|
||||
if(swap_tile->view != NULL) {
|
||||
view_maximize(swap_tile->view, &swap_tile->tile);
|
||||
view_damage_whole(swap_tile->view);
|
||||
} else {
|
||||
wlr_output_damage_add_box(tile->workspace->output->damage, &tile->tile);
|
||||
}
|
||||
|
||||
seat_set_focus(server->seat, swap_tile->view);
|
||||
}
|
||||
|
||||
void
|
||||
swap_tile_left(struct cg_tile *tile) {
|
||||
swap_tile(tile, find_left_tile);
|
||||
}
|
||||
|
||||
void
|
||||
swap_tile_right(struct cg_tile *tile) {
|
||||
swap_tile(tile, find_right_tile);
|
||||
}
|
||||
|
||||
void
|
||||
swap_tile_top(struct cg_tile *tile) {
|
||||
swap_tile(tile, find_top_tile);
|
||||
}
|
||||
|
||||
void
|
||||
swap_tile_bottom(struct cg_tile *tile) {
|
||||
swap_tile(tile, find_bottom_tile);
|
||||
}
|
||||
|
||||
void
|
||||
focus_tile(struct cg_tile *tile,
|
||||
struct cg_tile *(*find_tile)(const struct cg_tile *tile)) {
|
||||
struct cg_tile *new_tile = find_tile(tile);
|
||||
if(new_tile == NULL) {
|
||||
return;
|
||||
}
|
||||
struct cg_server *server = new_tile->workspace->server;
|
||||
workspace_focus_tile(tile->workspace, new_tile);
|
||||
seat_set_focus(server->seat, new_tile->view);
|
||||
}
|
||||
|
||||
void
|
||||
focus_tile_left(struct cg_tile *tile) {
|
||||
focus_tile(tile, find_left_tile);
|
||||
}
|
||||
|
||||
void
|
||||
focus_tile_right(struct cg_tile *tile) {
|
||||
focus_tile(tile, find_right_tile);
|
||||
}
|
||||
|
||||
void
|
||||
focus_tile_top(struct cg_tile *tile) {
|
||||
focus_tile(tile, find_top_tile);
|
||||
}
|
||||
|
||||
void
|
||||
focus_tile_bottom(struct cg_tile *tile) {
|
||||
focus_tile(tile, find_bottom_tile);
|
||||
}
|
||||
|
||||
// Returns whether x is between a and b (a exclusive, b exclusive) where
|
||||
// a<b
|
||||
bool
|
||||
is_between_strict(int a, int b, int x) {
|
||||
return a < x && x < b;
|
||||
}
|
||||
|
||||
bool
|
||||
resize_allowed(struct cg_tile *tile, struct cg_tile *parent, int coord_offset,
|
||||
int dim_offset, int *(*get_coord)(struct cg_tile *tile),
|
||||
int *(*get_dim)(struct cg_tile *tile)) {
|
||||
if(coord_offset == 0 && dim_offset == 0) {
|
||||
return true;
|
||||
} else if(*get_dim(tile) - coord_offset + dim_offset <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(struct cg_tile *it = tile->next; it != tile && it != NULL;
|
||||
it = it->next) {
|
||||
if(it == parent) {
|
||||
continue;
|
||||
}
|
||||
if(it->tile.x == tile->tile.x + tile->tile.width) {
|
||||
if(!resize_allowed(it, tile, dim_offset, -dim_offset, get_coord,
|
||||
get_dim)) {
|
||||
return false;
|
||||
}
|
||||
} else if(it->tile.x + it->tile.width == tile->tile.x) {
|
||||
if(!resize_allowed(it, tile, 0, coord_offset, get_coord, get_dim)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
resize(struct cg_tile *tile, const struct cg_tile *parent, int coord_offset,
|
||||
int dim_offset, int *(*get_coord)(struct cg_tile *tile),
|
||||
int *(*get_dim)(struct cg_tile *tile)) {
|
||||
if(coord_offset == 0 && dim_offset == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(struct cg_tile *it = tile->next; it != tile && it != NULL;
|
||||
it = it->next) {
|
||||
if(it == parent) {
|
||||
continue;
|
||||
}
|
||||
if(*get_coord(it) == *get_coord(tile) + *get_dim(tile)) {
|
||||
resize(it, tile, dim_offset, -dim_offset, get_coord, get_dim);
|
||||
} else if(*get_coord(it) + *get_dim(it) == *get_coord(tile)) {
|
||||
resize(it, tile, 0, coord_offset, get_coord, get_dim);
|
||||
}
|
||||
}
|
||||
|
||||
int old_x = tile->tile.x, old_y = tile->tile.y;
|
||||
*get_coord(tile) += coord_offset;
|
||||
*get_dim(tile) += dim_offset;
|
||||
struct wlr_box damage_box = {
|
||||
.x = old_x,
|
||||
.y = old_y,
|
||||
.width = tile->tile.x - old_x == 0 ? tile->tile.width : coord_offset,
|
||||
.height = tile->tile.y == 0 ? tile->tile.height : coord_offset};
|
||||
wlr_output_damage_add_box(tile->workspace->output->damage, &damage_box);
|
||||
if(tile->view != NULL) {
|
||||
view_maximize(tile->view, &tile->tile);
|
||||
}
|
||||
}
|
||||
|
||||
int *
|
||||
get_width(struct cg_tile *tile) {
|
||||
return &tile->tile.width;
|
||||
}
|
||||
|
||||
int *
|
||||
get_height(struct cg_tile *tile) {
|
||||
return &tile->tile.height;
|
||||
}
|
||||
|
||||
int *
|
||||
get_x(struct cg_tile *tile) {
|
||||
return &tile->tile.x;
|
||||
}
|
||||
|
||||
int *
|
||||
get_y(struct cg_tile *tile) {
|
||||
return &tile->tile.y;
|
||||
}
|
||||
|
||||
bool
|
||||
resize_allowed_horizontal(struct cg_tile *tile, struct cg_tile *parent,
|
||||
int x_offset, int width_offset) {
|
||||
return resize_allowed(tile, parent, x_offset, width_offset, get_x,
|
||||
get_width);
|
||||
}
|
||||
|
||||
bool
|
||||
resize_allowed_vertical(struct cg_tile *tile, struct cg_tile *parent,
|
||||
int y_offset, int height_offset) {
|
||||
return resize_allowed(tile, parent, y_offset, height_offset, get_y,
|
||||
get_height);
|
||||
}
|
||||
|
||||
void
|
||||
resize_horizontal(struct cg_tile *tile, struct cg_tile *parent, int x_offset,
|
||||
int width_offset) {
|
||||
resize(tile, parent, x_offset, width_offset, get_x, get_width);
|
||||
}
|
||||
|
||||
void
|
||||
resize_vertical(struct cg_tile *tile, struct cg_tile *parent, int y_offset,
|
||||
int height_offset) {
|
||||
resize(tile, parent, y_offset, height_offset, get_y, get_height);
|
||||
}
|
||||
|
||||
/* hpixs: positiv -> right, negative -> left; vpixs: positiv -> down, negative
|
||||
* -> up */
|
||||
void
|
||||
resize_tile(struct cg_server *server, int hpixs, int vpixs) {
|
||||
struct cg_output *output = server->curr_output;
|
||||
struct wlr_box *output_box = wlr_output_layout_get_box(
|
||||
output->server->output_layout, output->wlr_output);
|
||||
|
||||
struct cg_tile *focused =
|
||||
output->workspaces[output->curr_workspace]->focused_tile;
|
||||
/* First do the horizontal adjustment */
|
||||
if(hpixs != 0 && focused->tile.width < output_box->width &&
|
||||
is_between_strict(0, output_box->width, focused->tile.width + hpixs)) {
|
||||
int x_offset = 0;
|
||||
/* In case we are on the total right, move the left edge of the tile */
|
||||
if(focused->tile.x + focused->tile.width == output_box->width) {
|
||||
x_offset = -hpixs;
|
||||
}
|
||||
bool resize_allowed =
|
||||
resize_allowed_horizontal(focused, NULL, x_offset, hpixs);
|
||||
if(resize_allowed) {
|
||||
resize_horizontal(focused, NULL, x_offset, hpixs);
|
||||
}
|
||||
}
|
||||
/* Repeat for vertical */
|
||||
if(vpixs != 0 && focused->tile.height < output_box->height &&
|
||||
is_between_strict(0, output_box->height, focused->tile.height + vpixs)) {
|
||||
int y_offset = 0;
|
||||
if(focused->tile.y + focused->tile.height == output_box->height) {
|
||||
y_offset = -vpixs;
|
||||
}
|
||||
bool resize_allowed =
|
||||
resize_allowed_vertical(focused, NULL, y_offset, vpixs);
|
||||
if(resize_allowed) {
|
||||
resize_vertical(focused, NULL, y_offset, vpixs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
keybinding_workspace_fullscreen(struct cg_server *server) {
|
||||
struct cg_view *current_view = seat_get_focus(server->seat);
|
||||
struct cg_output *output = server->curr_output;
|
||||
|
||||
/* We are focused on the background */
|
||||
if(current_view == NULL) {
|
||||
struct cg_view *it = NULL;
|
||||
wl_list_for_each(it, &output->workspaces[output->curr_workspace]->views,
|
||||
link) {
|
||||
if(view_is_visible(it)) {
|
||||
current_view = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workspace_free_tiles(output->workspaces[output->curr_workspace]);
|
||||
full_screen_workspace_tiles(output,
|
||||
output->workspaces[output->curr_workspace]);
|
||||
|
||||
seat_set_focus(server->seat, current_view);
|
||||
}
|
||||
|
||||
// Switch to a differerent virtual terminal
|
||||
static int
|
||||
keybinding_switch_vt(struct wlr_backend* backend, unsigned int vt) {
|
||||
if(wlr_backend_is_multi(backend)) {
|
||||
struct wlr_session* session = wlr_backend_get_session(backend);
|
||||
if(session) {
|
||||
wlr_session_change_vt(session, vt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Split screen (vertical or horizontal)
|
||||
* Important: Do not attempt to perform mathematical simplifications in this
|
||||
* function without taking rounding errors into account. */
|
||||
static void
|
||||
keybinding_split_output(struct cg_output *output, bool vertical) {
|
||||
struct cg_view *original_view = seat_get_focus(output->server->seat);
|
||||
struct cg_workspace *curr_workspace =
|
||||
output->workspaces[output->curr_workspace];
|
||||
int32_t width = curr_workspace->focused_tile->tile.width;
|
||||
int32_t height = curr_workspace->focused_tile->tile.height;
|
||||
int32_t x = curr_workspace->focused_tile->tile.x;
|
||||
int32_t y = curr_workspace->focused_tile->tile.y;
|
||||
int32_t new_width, new_height;
|
||||
|
||||
if(vertical) {
|
||||
new_width = width / 2;
|
||||
new_height = height;
|
||||
} else {
|
||||
new_width = width;
|
||||
new_height = height / 2;
|
||||
}
|
||||
if(new_width < 1 || new_height < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct cg_view *next_view = NULL;
|
||||
struct cg_view *it;
|
||||
wl_list_for_each(it, &output->workspaces[output->curr_workspace]->views,
|
||||
link) {
|
||||
if(!view_is_visible(it) && original_view != it) {
|
||||
next_view = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t new_x, new_y;
|
||||
if(vertical) {
|
||||
new_x = x + new_width;
|
||||
new_y = y;
|
||||
} else {
|
||||
new_x = x;
|
||||
new_y = y + new_height;
|
||||
}
|
||||
|
||||
struct cg_tile *new_tile = calloc(1, sizeof(struct cg_tile));
|
||||
new_tile->tile.x = new_x;
|
||||
new_tile->tile.y = new_y;
|
||||
new_tile->tile.width = x + width - new_x;
|
||||
new_tile->tile.height = y + height - new_y;
|
||||
new_tile->prev = curr_workspace->focused_tile;
|
||||
new_tile->next = curr_workspace->focused_tile->next;
|
||||
new_tile->view = next_view;
|
||||
new_tile->workspace = curr_workspace;
|
||||
curr_workspace->focused_tile->next->prev = new_tile;
|
||||
curr_workspace->focused_tile->next = new_tile;
|
||||
|
||||
curr_workspace->focused_tile->tile.width = new_width;
|
||||
curr_workspace->focused_tile->tile.height = new_height;
|
||||
workspace_focus_tile(curr_workspace, curr_workspace->focused_tile);
|
||||
|
||||
if(next_view != NULL) {
|
||||
view_maximize(next_view, &new_tile->tile);
|
||||
}
|
||||
|
||||
if(original_view != NULL) {
|
||||
view_maximize(original_view, &curr_workspace->focused_tile->tile);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
keybinding_split_vertical(struct cg_server *server) {
|
||||
keybinding_split_output(server->curr_output, true);
|
||||
}
|
||||
|
||||
static void
|
||||
keybinding_split_horizontal(struct cg_server *server) {
|
||||
keybinding_split_output(server->curr_output, false);
|
||||
}
|
||||
|
||||
static void
|
||||
into_process(const char *command) {
|
||||
execl("/bin/sh", "sh", "-c", command, (char *)NULL);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
keybinding_cycle_outputs(struct cg_server *server, bool reverse) {
|
||||
if(reverse) {
|
||||
server->curr_output = wl_container_of(server->curr_output->link.prev,
|
||||
server->curr_output, link);
|
||||
} else {
|
||||
server->curr_output = wl_container_of(server->curr_output->link.next,
|
||||
server->curr_output, link);
|
||||
}
|
||||
if(&server->curr_output->link == &server->outputs) {
|
||||
if(reverse) {
|
||||
server->curr_output = wl_container_of(
|
||||
server->curr_output->link.prev, server->curr_output, link);
|
||||
} else {
|
||||
server->curr_output = wl_container_of(
|
||||
server->curr_output->link.next, server->curr_output, link);
|
||||
}
|
||||
}
|
||||
seat_set_focus(
|
||||
server->seat,
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile->view);
|
||||
message_printf(server->curr_output, "Current Output");
|
||||
}
|
||||
|
||||
/* Cycle through views, whereby the workspace does not change */
|
||||
void
|
||||
keybinding_cycle_views(struct cg_server *server, bool reverse) {
|
||||
struct cg_workspace *curr_workspace =
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace];
|
||||
struct cg_view *current_view = curr_workspace->focused_tile->view;
|
||||
|
||||
struct cg_view *new_view;
|
||||
// We are focused on the desktop
|
||||
if(current_view == NULL) {
|
||||
current_view = wl_container_of(&curr_workspace->views, new_view, link);
|
||||
if(reverse) {
|
||||
new_view =
|
||||
wl_container_of(curr_workspace->views.prev, new_view, link);
|
||||
} else {
|
||||
new_view =
|
||||
wl_container_of(curr_workspace->views.next, new_view, link);
|
||||
}
|
||||
} else {
|
||||
if(reverse) {
|
||||
new_view = wl_container_of(current_view->link.prev, new_view, link);
|
||||
} else {
|
||||
new_view = wl_container_of(current_view->link.next, new_view, link);
|
||||
}
|
||||
}
|
||||
while(&new_view->link != ¤t_view->link &&
|
||||
(&new_view->link == &curr_workspace->views ||
|
||||
view_is_visible(new_view))) {
|
||||
if(reverse) {
|
||||
new_view = wl_container_of(new_view->link.prev, new_view, link);
|
||||
} else {
|
||||
new_view = wl_container_of(new_view->link.next, new_view, link);
|
||||
}
|
||||
}
|
||||
if(&new_view->link == &curr_workspace->views) {
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_output_damage_add_box(curr_workspace->output->damage, &curr_workspace->focused_tile->tile);
|
||||
seat_set_focus(server->seat, new_view);
|
||||
/* Move the previous view to the end of the list unless we are focused on
|
||||
* the desktop*/
|
||||
if(!reverse && ¤t_view->link != &curr_workspace->views) {
|
||||
wl_list_remove(¤t_view->link);
|
||||
wl_list_insert(curr_workspace->views.prev, ¤t_view->link);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
keybinding_cycle_tiles(struct cg_server *server, bool reverse) {
|
||||
struct cg_output *output = server->curr_output;
|
||||
struct cg_workspace *workspace = output->workspaces[output->curr_workspace];
|
||||
if(reverse) {
|
||||
workspace_focus_tile(workspace, workspace->focused_tile->prev);
|
||||
} else {
|
||||
workspace_focus_tile(workspace, workspace->focused_tile->next);
|
||||
}
|
||||
struct cg_view *next_view = workspace->focused_tile->view;
|
||||
|
||||
seat_set_focus(server->seat, next_view);
|
||||
}
|
||||
|
||||
int
|
||||
keybinding_switch_ws(struct cg_server *server, uint32_t ws) {
|
||||
if(ws > server->nws) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Requested workspace %u, but only have %u workspaces.", ws,
|
||||
server->nws);
|
||||
return -1;
|
||||
}
|
||||
struct cg_output *output = server->curr_output;
|
||||
output->curr_workspace = ws;
|
||||
seat_set_focus(server->seat,
|
||||
server->curr_output->workspaces[ws]->focused_tile->view);
|
||||
wlr_output_damage_add_whole(output->damage);
|
||||
message_printf(server->curr_output, "Workspace %d", ws + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
keybinding_show_time(struct cg_server *server) {
|
||||
char *msg, *tmp;
|
||||
time_t timep;
|
||||
|
||||
timep = time(NULL);
|
||||
tmp = ctime(&timep);
|
||||
msg = strdup(tmp);
|
||||
msg[strcspn(msg, "\n")] = '\0'; /* Remove the newline */
|
||||
|
||||
message_printf(server->curr_output, "%s", msg);
|
||||
}
|
||||
|
||||
void
|
||||
keybinding_move_view_to_next_output(struct cg_server *server) {
|
||||
if(wl_list_length(&server->outputs) <= 1) {
|
||||
return;
|
||||
}
|
||||
struct cg_view *view =
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile->view;
|
||||
if(view != NULL) {
|
||||
wl_list_remove(&view->link);
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile->view = NULL;
|
||||
keybinding_cycle_views(server, false);
|
||||
view_damage_whole(view);
|
||||
if(server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile->view == NULL) {
|
||||
seat_set_focus(server->seat, NULL);
|
||||
}
|
||||
}
|
||||
keybinding_cycle_outputs(server, false);
|
||||
if(view != NULL) {
|
||||
wl_list_insert(&server->curr_output
|
||||
->workspaces[server->curr_output->curr_workspace]
|
||||
->views,
|
||||
&view->link);
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile->view = view;
|
||||
view->workspace = server->curr_output
|
||||
->workspaces[server->curr_output->curr_workspace];
|
||||
view_position(view);
|
||||
seat_set_focus(server->seat, view);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
keybinding_move_view_to_workspace(struct cg_server *server, uint32_t ws) {
|
||||
struct cg_view *view =
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile->view;
|
||||
if(view != NULL) {
|
||||
wl_list_remove(&view->link);
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile->view = NULL;
|
||||
keybinding_cycle_views(server, false);
|
||||
if(server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile->view == NULL) {
|
||||
seat_set_focus(server->seat, NULL);
|
||||
}
|
||||
}
|
||||
keybinding_switch_ws(server, ws);
|
||||
if(view != NULL) {
|
||||
struct cg_workspace *ws =
|
||||
server->curr_output
|
||||
->workspaces[server->curr_output->curr_workspace];
|
||||
view->workspace = ws;
|
||||
wl_list_insert(&ws->views, &view->link);
|
||||
ws->focused_tile->view = view;
|
||||
view_maximize(view, &ws->focused_tile->tile);
|
||||
seat_set_focus(server->seat, view);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hint: see keybinding.h for details on "data" */
|
||||
int
|
||||
run_action(enum keybinding_action action, struct cg_server *server,
|
||||
union keybinding_params data) {
|
||||
switch(action) {
|
||||
case KEYBINDING_QUIT:
|
||||
display_terminate(server);
|
||||
break;
|
||||
case KEYBINDING_CHANGE_TTY:
|
||||
return keybinding_switch_vt(server->backend, data.u);
|
||||
case KEYBINDING_LAYOUT_FULLSCREEN:
|
||||
keybinding_workspace_fullscreen(server);
|
||||
break;
|
||||
case KEYBINDING_SPLIT_HORIZONTAL:
|
||||
keybinding_split_horizontal(server);
|
||||
break;
|
||||
case KEYBINDING_SPLIT_VERTICAL:
|
||||
keybinding_split_vertical(server);
|
||||
break;
|
||||
case KEYBINDING_RUN_COMMAND:
|
||||
if(fork() == 0) {
|
||||
into_process(data.c);
|
||||
}
|
||||
break;
|
||||
case KEYBINDING_CYCLE_VIEWS:
|
||||
keybinding_cycle_views(server, data.b);
|
||||
break;
|
||||
case KEYBINDING_CYCLE_TILES:
|
||||
keybinding_cycle_tiles(server, data.b);
|
||||
break;
|
||||
case KEYBINDING_CYCLE_OUTPUT:
|
||||
keybinding_cycle_outputs(server, data.b);
|
||||
break;
|
||||
case KEYBINDING_SWITCH_WORKSPACE:
|
||||
keybinding_switch_ws(server, data.u);
|
||||
break;
|
||||
case KEYBINDING_SWITCH_MODE:
|
||||
server->seat->mode = data.u;
|
||||
break;
|
||||
case KEYBINDING_SWITCH_DEFAULT_MODE:
|
||||
server->seat->mode = data.u;
|
||||
server->seat->default_mode = data.u;
|
||||
break;
|
||||
case KEYBINDING_NOOP:
|
||||
break;
|
||||
case KEYBINDING_SHOW_TIME:
|
||||
keybinding_show_time(server);
|
||||
break;
|
||||
case KEYBINDING_RESIZE_TILE_HORIZONTAL:
|
||||
resize_tile(server, data.i, 0);
|
||||
break;
|
||||
case KEYBINDING_RESIZE_TILE_VERTICAL:
|
||||
resize_tile(server, 0, data.i);
|
||||
break;
|
||||
case KEYBINDING_MOVE_VIEW_TO_WORKSPACE: {
|
||||
keybinding_move_view_to_workspace(server, data.u);
|
||||
break;
|
||||
}
|
||||
case KEYBINDING_SWAP_LEFT: {
|
||||
swap_tile_left(
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile);
|
||||
break;
|
||||
}
|
||||
case KEYBINDING_SWAP_RIGHT: {
|
||||
swap_tile_right(
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile);
|
||||
break;
|
||||
}
|
||||
case KEYBINDING_SWAP_TOP: {
|
||||
swap_tile_top(
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile);
|
||||
break;
|
||||
}
|
||||
case KEYBINDING_SWAP_BOTTOM: {
|
||||
swap_tile_bottom(
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile);
|
||||
break;
|
||||
}
|
||||
case KEYBINDING_FOCUS_LEFT: {
|
||||
focus_tile_left(
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile);
|
||||
break;
|
||||
}
|
||||
case KEYBINDING_FOCUS_RIGHT: {
|
||||
focus_tile_right(
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile);
|
||||
break;
|
||||
}
|
||||
case KEYBINDING_FOCUS_TOP: {
|
||||
focus_tile_top(
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile);
|
||||
break;
|
||||
}
|
||||
case KEYBINDING_FOCUS_BOTTOM: {
|
||||
focus_tile_bottom(
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace]
|
||||
->focused_tile);
|
||||
break;
|
||||
}
|
||||
case KEYBINDING_MOVE_VIEW_TO_NEXT_OUTPUT: {
|
||||
keybinding_move_view_to_next_output(server);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
wlr_log(WLR_ERROR,
|
||||
"run_action was called with a value not present in \"enum "
|
||||
"keybinding_action\". This should not happen.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
86
keybinding.h
Normal file
86
keybinding.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#ifndef KEYBINDING_H
|
||||
|
||||
#define KEYBINDING_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <wlr/types/wlr_keyboard.h>
|
||||
|
||||
struct cg_server;
|
||||
|
||||
/* 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"
|
||||
* in keybinding.c */
|
||||
enum keybinding_action {
|
||||
KEYBINDING_RUN_COMMAND, // data.c is the string to execute
|
||||
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_QUIT,
|
||||
KEYBINDING_NOOP,
|
||||
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_NEXT_OUTPUT,
|
||||
KEYBINDING_SHOW_TIME,
|
||||
|
||||
KEYBINDING_SWAP_LEFT,
|
||||
KEYBINDING_SWAP_RIGHT,
|
||||
KEYBINDING_SWAP_TOP,
|
||||
KEYBINDING_SWAP_BOTTOM,
|
||||
|
||||
KEYBINDING_FOCUS_LEFT,
|
||||
KEYBINDING_FOCUS_RIGHT,
|
||||
KEYBINDING_FOCUS_TOP,
|
||||
KEYBINDING_FOCUS_BOTTOM,
|
||||
};
|
||||
|
||||
union keybinding_params {
|
||||
char *c;
|
||||
uint32_t u;
|
||||
int32_t i;
|
||||
bool b;
|
||||
};
|
||||
|
||||
struct keybinding {
|
||||
uint16_t mode;
|
||||
xkb_mod_mask_t modifiers;
|
||||
xkb_keysym_t key;
|
||||
enum keybinding_action action;
|
||||
union keybinding_params data; // See enum keybinding_action for details
|
||||
};
|
||||
|
||||
struct keybinding_list {
|
||||
uint32_t length;
|
||||
uint32_t capacity;
|
||||
struct keybinding **keybindings;
|
||||
};
|
||||
|
||||
int
|
||||
keybinding_list_push(struct keybinding_list *list,
|
||||
struct keybinding *keybinding);
|
||||
void
|
||||
keybinding_list_free(struct keybinding_list *list);
|
||||
void
|
||||
keybinding_cycle_outputs(struct cg_server *server, bool reverse);
|
||||
struct keybinding **
|
||||
find_keybinding(const struct keybinding_list *list,
|
||||
const struct keybinding *keybinding);
|
||||
struct keybinding_list *
|
||||
keybinding_list_init();
|
||||
|
||||
int
|
||||
run_action(enum keybinding_action action, struct cg_server *server,
|
||||
union keybinding_params data);
|
||||
|
||||
#endif /* end of include guard KEYBINDINGS_H */
|
||||
65
keys/cagebreak_signing_key_1.asc
Normal file
65
keys/cagebreak_signing_key_1.asc
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
Rw==
|
||||
=IZJF
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
64
keys/cagebreak_signing_key_2.asc
Normal file
64
keys/cagebreak_signing_key_2.asc
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
MI0SwN7gPGlOudIlzBJwygPPhSnZXtDYJA==
|
||||
=Dz6g
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
266
man/cagebreak-config.5.md
Normal file
266
man/cagebreak-config.5.md
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
% CAGEBREAK-CONFIG(1) Version 1.0 | Cagebreak Manual
|
||||
|
||||
# NAME
|
||||
|
||||
**cagebreak-config** — Cagebreak config file
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**\$XDG_CONFIG_PATH/cagebreak/config**
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The `cagebreak` configuration file is a simple plain text file which configures
|
||||
the way `cagebreak` behaves.
|
||||
Each line constitutes a separate command which is parsed
|
||||
independently from the rest of the file. Comments can be added
|
||||
by prepending a line with the # symbol.
|
||||
|
||||
## COMMANDS
|
||||
|
||||
**background - Set background color**
|
||||
|
||||
> Set the background color. This command expects three floating point numbers
|
||||
> between 0 and 1, specifying the r,g and b values respectively.
|
||||
> (e.g. "background 1.0 0.0 0.0" sets to background color to red)
|
||||
> There is no support for specifying a background image.
|
||||
|
||||
**bind - Bind key to action in command mode**
|
||||
|
||||
> This command requires a key (see **KEY DEFINITIONS**) and an action (see **ACTIONS**) as an argument.
|
||||
> Subsequently, pressing this key while in command mode executes the
|
||||
> supplied action. `bind <key> <keybinding>` is equivalent to
|
||||
> `definekey root <key> <action>`
|
||||
|
||||
**definekey - Bind key to action in arbitrary mode**
|
||||
|
||||
> This command behaves similarly to the **bind** command with the
|
||||
> difference that the mode in which the keybinding is activated is
|
||||
> specified by the user. A call to this function is to be structured as follows:
|
||||
>
|
||||
>> `definekey <mode> <key> <action>`
|
||||
|
||||
**definemode**
|
||||
|
||||
> This command requires a single argument; the name of the mode to be defined.
|
||||
> Subsequent to a call to this function, the defined mode may be used along with
|
||||
> the definekey command to create a custom keymapping. Synopsis:
|
||||
>
|
||||
>> `definemode <mode>`
|
||||
|
||||
**escape**
|
||||
|
||||
> Defines the key with which the current mode can be changed to "root".
|
||||
> `escape <key>` is equivalent to `definekey top <key> switch_mode root`
|
||||
|
||||
**exec**
|
||||
|
||||
> Executes the supplied shell command using *`sh -c "<command>"`*. Synopsis:
|
||||
>
|
||||
>> `exec <command>`
|
||||
|
||||
**workspaces**
|
||||
|
||||
> Requires a single integer larger than 1 and less than 30 as an argument. Sets the number of
|
||||
> workspaces to the supplied number
|
||||
|
||||
# ACTIONS
|
||||
|
||||
**vsplit**
|
||||
|
||||
> Split current tile vertically
|
||||
|
||||
**hsplit**
|
||||
|
||||
> Split current tile horizontally
|
||||
|
||||
**quit**
|
||||
|
||||
> Exit cagebreak
|
||||
|
||||
**focus**
|
||||
|
||||
> Focus next tile
|
||||
|
||||
**focusprev**
|
||||
|
||||
> Focus previous tile
|
||||
|
||||
**next**
|
||||
|
||||
> Focus next window in current tile
|
||||
|
||||
**prev**
|
||||
|
||||
> Focus previous window in current tile
|
||||
|
||||
**only**
|
||||
|
||||
> Remove all splits and make the current window fill the entire screen
|
||||
|
||||
**abort**
|
||||
|
||||
> Return to the default mode without running any action
|
||||
|
||||
**time**
|
||||
|
||||
> Display time
|
||||
|
||||
**nextscreen**
|
||||
|
||||
> Focus the next screen
|
||||
|
||||
**prevscreen**
|
||||
|
||||
> Focus the previous screen
|
||||
|
||||
**resizeleft**
|
||||
|
||||
> Resize the current tile towards the left
|
||||
|
||||
**resizeright**
|
||||
|
||||
> Resize the current tile towards the right
|
||||
|
||||
**resizedown**
|
||||
|
||||
> Resize the current tile towards the bottom
|
||||
|
||||
**resizeup**
|
||||
|
||||
> Resize the current tile towards the top
|
||||
|
||||
**workspace <n>**
|
||||
|
||||
> Change to the n-th workspace
|
||||
|
||||
**movetoworkspace <n>**
|
||||
|
||||
> Move the currently focused window to the n-th workspace
|
||||
|
||||
**exchangeleft**
|
||||
|
||||
> Exchange the current window with the window in the tile to the left
|
||||
|
||||
**exchangeright**
|
||||
|
||||
> Exchange the current window with the window in the tile to the right
|
||||
|
||||
**exchangeup**
|
||||
|
||||
> Exchange the current window with the window in the tile to the top
|
||||
|
||||
**exchangedown**
|
||||
|
||||
> Exchange the current window with the window in the tile to the bottom
|
||||
|
||||
**focusleft**
|
||||
|
||||
> Focus the tile to the left
|
||||
|
||||
**focusright**
|
||||
|
||||
> Focus the tile to the right
|
||||
|
||||
**focusup**
|
||||
|
||||
> Focus the tile to the top
|
||||
|
||||
**focusdown**
|
||||
|
||||
> Focus the tile to the bottom
|
||||
|
||||
**movetonextscreen**
|
||||
|
||||
> Move the current window to the next screen
|
||||
|
||||
**switchvt <n>**
|
||||
|
||||
> Switch to tty n
|
||||
|
||||
**mode <mode>**
|
||||
|
||||
> Enter mode "`<mode>`". After a keybinding is processed, return to default mode
|
||||
|
||||
**setmode <mode>**
|
||||
|
||||
> Set the default mode to `<mode>`
|
||||
|
||||
# MODES
|
||||
|
||||
By default, three modes are defined:
|
||||
|
||||
**top**
|
||||
|
||||
> The default mode. Keybindings defined in this mode can be accessed
|
||||
> directly, without the use of an escape key.
|
||||
|
||||
**root**
|
||||
|
||||
> The command mode. Keybindings defined in this mode can be accessed
|
||||
> after pressing the key defined by the `escape` command.
|
||||
|
||||
**resize**
|
||||
|
||||
> Resize mode. This mode is used for resizing tiles.
|
||||
|
||||
# KEY DEFINITIONS
|
||||
|
||||
Keys are specified by their names as displayed for example by *`xev`*.
|
||||
In addition, modifiers can be specified using the following syntax:
|
||||
|
||||
> `<mod>-<key>`
|
||||
|
||||
The supported modifiers are:
|
||||
|
||||
**A - Alt**
|
||||
|
||||
**C - Control**
|
||||
|
||||
**L - Logo**
|
||||
|
||||
**S - Shift**
|
||||
|
||||
**2 - Mod2**
|
||||
|
||||
**3 - Mod 3**
|
||||
|
||||
**5 - Mod 5**
|
||||
|
||||
For example to specify the keybinding Control+t, the expression:
|
||||
|
||||
>`C-t`
|
||||
|
||||
is used.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
**cagebreak(1)**
|
||||
|
||||
# BUGS
|
||||
|
||||
See GitHub Issues: <https://github.com/project-repo/cagebreak/issues>
|
||||
|
||||
# LICENSE
|
||||
|
||||
Copyright (c) 2020 The Cagebreak authors
|
||||
Copyright (c) 2018-2020 Jente Hidskes
|
||||
Copyright (c) 2019 The Sway 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.
|
||||
90
man/cagebreak.1.md
Normal file
90
man/cagebreak.1.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
% CAGEBREAK(1) Version 1.0 | Cagebreak Manual
|
||||
|
||||
# NAME
|
||||
|
||||
**cagebreak** — A Wayland tiling compositor to the likes of ratpoison
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
| **cagebreak** [OPTIONS]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
`cagebreak` is a slim, keyboard-controlled, tiling compositor for
|
||||
wayland conceptually based on the X11 window manager `ratpoison`.
|
||||
|
||||
It allows for the screen to be split into non-overlapping tiles and,
|
||||
in contrast to the original `ratpoison`, has native support for
|
||||
multi-workspace operation.
|
||||
|
||||
All interactions between the user and `cagebreak` are done via
|
||||
the keyboard. Configuration of this behaviour is specified
|
||||
in the **\$XDG_CONFIG_PATH/cagebreak/config** file (See **cagebreak-config(5)**).
|
||||
|
||||
## OPTIONS
|
||||
|
||||
-h
|
||||
|
||||
: Display help message and exit
|
||||
|
||||
-r
|
||||
|
||||
: Rotate the output 90 degrees clockwise, can be specified up to three times
|
||||
|
||||
-v
|
||||
|
||||
: Show version number and exit
|
||||
|
||||
# ENVIRONMENT
|
||||
|
||||
`XKB_DEFAULT_LAYOUT`
|
||||
|
||||
: The keyboard layout to be used (See **xkeyboard-config(7)**)
|
||||
|
||||
`XKB_DEFAULT_MODEL`
|
||||
|
||||
: The keyboard model to be used (See **xkeyboard-config(7)**)
|
||||
|
||||
`XKB_DEFAULT_VARIANT`
|
||||
|
||||
: The keyboard variant to be used (See **xkeyboard-config(7)**)
|
||||
|
||||
`XKB_DEFAULT_RULES`
|
||||
|
||||
: The xkb rules to be used
|
||||
|
||||
`XKB_DEFAULT_OPTIONS`
|
||||
|
||||
: The xkb options to be used
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
**cagebreak-config(5)**
|
||||
|
||||
# BUGS
|
||||
|
||||
See GitHub Issues: <https://github.com/project-repo/cagebreak/issues>
|
||||
|
||||
# LICENSE
|
||||
|
||||
Copyright (c) 2020 The Cagebreak authors
|
||||
Copyright (c) 2018-2020 Jente Hidskes
|
||||
Copyright (c) 2019 The Sway 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.
|
||||
231
meson.build
Normal file
231
meson.build
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
project('cagebreak', 'c',
|
||||
version: '1.0.0',
|
||||
license: 'MIT',
|
||||
default_options: [
|
||||
'c_std=c11',
|
||||
'warning_level=3',
|
||||
],
|
||||
)
|
||||
|
||||
add_project_arguments(
|
||||
[
|
||||
'-DWLR_USE_UNSTABLE',
|
||||
'-Wall',
|
||||
'-Wundef',
|
||||
'-Wno-unused-parameter',
|
||||
],
|
||||
language: 'c',
|
||||
)
|
||||
|
||||
if get_option('buildtype').startswith('debug')
|
||||
add_project_arguments(
|
||||
[
|
||||
'-DDEBUG',
|
||||
'-g',
|
||||
],
|
||||
language : 'c',
|
||||
)
|
||||
endif
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
fuzz_compile_args = []
|
||||
fuzz_link_args = []
|
||||
if get_option('fuzz')
|
||||
fuzzing_engine = meson.get_compiler('c').find_library('Fuzzer', required : false)
|
||||
if fuzzing_engine.found()
|
||||
fuzz_compile_args += '-fsanitize-coverage=trace-pc-guard,trace-cmp'
|
||||
elif cc.has_argument('-fsanitize=fuzzer-no-link')
|
||||
fuzz_compile_args += '-fsanitize=fuzzer-no-link'
|
||||
fuzz_link_args += '-fsanitize=fuzzer-no-link'
|
||||
else
|
||||
error('Looks like neither libFuzzer nor -fsanitize=fuzzer-no-link is supported. Disable fuzzing using -Dfuzz=false to build without fuzzers.')
|
||||
endif
|
||||
endif
|
||||
|
||||
is_freebsd = host_machine.system().startswith('freebsd')
|
||||
if is_freebsd
|
||||
add_project_arguments(
|
||||
[
|
||||
'-Wno-format-extra-args',
|
||||
'-Wno-gnu-zero-variadic-macro-arguments',
|
||||
],
|
||||
language: 'c'
|
||||
)
|
||||
endif
|
||||
|
||||
wlroots = dependency('wlroots', version: '>= 0.9.1')
|
||||
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
|
||||
wayland_server = dependency('wayland-server')
|
||||
wayland_cursor = dependency('wayland-cursor')
|
||||
wayland_client = dependency('wayland-client')
|
||||
pixman = dependency('pixman-1')
|
||||
xkbcommon = dependency('xkbcommon')
|
||||
cairo = dependency('cairo')
|
||||
pango = dependency('pango')
|
||||
pangocairo = dependency('pangocairo')
|
||||
math = cc.find_library('m')
|
||||
|
||||
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
|
||||
wayland_scanner = find_program('wayland-scanner')
|
||||
wayland_scanner_server = generator(
|
||||
wayland_scanner,
|
||||
output: '@BASENAME@-protocol.h',
|
||||
arguments: ['server-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
server_protocols = [
|
||||
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
|
||||
]
|
||||
|
||||
server_protos_headers = []
|
||||
|
||||
foreach p : server_protocols
|
||||
xml = join_paths(p)
|
||||
server_protos_headers += wayland_scanner_server.process(xml)
|
||||
endforeach
|
||||
|
||||
server_protos = declare_dependency(
|
||||
sources: server_protos_headers,
|
||||
)
|
||||
|
||||
if get_option('xwayland')
|
||||
wlroots_has_xwayland = cc.get_define('WLR_HAS_XWAYLAND', prefix: '#include <wlr/config.h>', dependencies: wlroots) == '1'
|
||||
if not wlroots_has_xwayland
|
||||
error('Cannot build Cagebreak with XWayland support: wlroots has been built without it')
|
||||
else
|
||||
have_xwayland = true
|
||||
endif
|
||||
else
|
||||
have_xwayland = false
|
||||
endif
|
||||
|
||||
git = find_program('git', native: true, required: false)
|
||||
if git.found()
|
||||
git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'])
|
||||
git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'])
|
||||
if git_commit.returncode() == 0 and git_branch.returncode() == 0
|
||||
version = '@0@-@1@ (branch \'@2@\')'.format(
|
||||
meson.project_version(),
|
||||
git_commit.stdout().strip(),
|
||||
git_branch.stdout().strip(),
|
||||
)
|
||||
endif
|
||||
else
|
||||
version = '@0@'.format(meson.project_version())
|
||||
endif
|
||||
|
||||
conf_data = configuration_data()
|
||||
conf_data.set10('CG_HAS_XWAYLAND', have_xwayland)
|
||||
conf_data.set_quoted('CG_VERSION', version)
|
||||
|
||||
|
||||
cagebreak_main_file = [ 'cagebreak.c', ]
|
||||
cagebreak_source_strings = [
|
||||
'idle_inhibit_v1.c',
|
||||
'keybinding.c',
|
||||
'workspace.c',
|
||||
'output.c',
|
||||
'parse.c',
|
||||
'render.c',
|
||||
'seat.c',
|
||||
'util.c',
|
||||
'view.c',
|
||||
'xdg_shell.c',
|
||||
'server.c',
|
||||
'message.c',
|
||||
'pango.c',
|
||||
]
|
||||
|
||||
cagebreak_header_strings = [
|
||||
'idle_inhibit_v1.h',
|
||||
'keybinding.h',
|
||||
'workspace.h',
|
||||
'output.h',
|
||||
'parse.h',
|
||||
'render.h',
|
||||
'seat.h',
|
||||
'server.h',
|
||||
'util.h',
|
||||
'view.h',
|
||||
'xdg_shell.h',
|
||||
'pango.h',
|
||||
'message.h',
|
||||
]
|
||||
|
||||
if conf_data.get('CG_HAS_XWAYLAND', 0) == 1
|
||||
cagebreak_source_strings += 'xwayland.c'
|
||||
cagebreak_header_strings += 'xwayland.h'
|
||||
endif
|
||||
|
||||
cagebreak_sources = [
|
||||
configure_file(input: 'config.h.in',
|
||||
output: 'config.h',
|
||||
configuration: conf_data),
|
||||
]
|
||||
|
||||
cagebreak_headers = []
|
||||
|
||||
foreach source : cagebreak_source_strings
|
||||
cagebreak_sources += files(source)
|
||||
endforeach
|
||||
|
||||
foreach header : cagebreak_header_strings
|
||||
cagebreak_headers += files(header)
|
||||
endforeach
|
||||
|
||||
cagebreak_dependencies = [
|
||||
server_protos,
|
||||
wayland_server,
|
||||
wayland_client,
|
||||
wayland_cursor,
|
||||
wlroots,
|
||||
xkbcommon,
|
||||
pixman,
|
||||
math,
|
||||
pango,
|
||||
cairo,
|
||||
pangocairo,
|
||||
]
|
||||
|
||||
executable(
|
||||
meson.project_name(),
|
||||
cagebreak_main_file + cagebreak_sources + cagebreak_headers,
|
||||
dependencies: cagebreak_dependencies,
|
||||
install: true,
|
||||
link_args: fuzz_link_args,
|
||||
c_args: fuzz_compile_args,
|
||||
)
|
||||
|
||||
pandoc = find_program('pandoc')
|
||||
mandir1 = join_paths(get_option('mandir'), 'man1')
|
||||
mandir5 = join_paths(get_option('mandir'), 'man5')
|
||||
|
||||
cagebreak_man = custom_target('cagebreak_man',
|
||||
output : 'cagebreak.1',
|
||||
input : 'man/cagebreak.1.md',
|
||||
command : [pandoc, '-i', '@INPUT@', '-o', '@OUTPUT@', '-f', 'markdown-smart', '-t', 'man', '-s'],
|
||||
install: true,
|
||||
install_dir: mandir1
|
||||
)
|
||||
|
||||
cagebreak_man = custom_target('cagebreak_config_man',
|
||||
output : 'cagebreak-config.5',
|
||||
input : 'man/cagebreak-config.5.md',
|
||||
command : [pandoc, '-i', '@INPUT@', '-o', '@OUTPUT@', '-f', 'markdown-smart', '-t', 'man', '-s'],
|
||||
install: true,
|
||||
install_dir: mandir5
|
||||
)
|
||||
|
||||
if get_option('fuzz')
|
||||
subdir('fuzz')
|
||||
endif
|
||||
|
||||
summary = [
|
||||
'',
|
||||
'Cagebreak @0@'.format(version),
|
||||
'',
|
||||
' xwayland: @0@'.format(have_xwayland),
|
||||
''
|
||||
]
|
||||
message('\n'.join(summary))
|
||||
2
meson_options.txt
Normal file
2
meson_options.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
option('xwayland', type: 'boolean', value: 'false', description: 'Enable support for X11 applications')
|
||||
option('fuzz', type: 'boolean', value: 'false', description: 'Enable building fuzzer targets')
|
||||
179
message.c
Normal file
179
message.c
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
#include "pango.h"
|
||||
#include <cairo/cairo.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_output_damage.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
|
||||
#include "cairo.h"
|
||||
#include "message.h"
|
||||
#include "output.h"
|
||||
#include "server.h"
|
||||
|
||||
cairo_subpixel_order_t
|
||||
to_cairo_subpixel_order(const enum wl_output_subpixel subpixel) {
|
||||
switch(subpixel) {
|
||||
case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
|
||||
return CAIRO_SUBPIXEL_ORDER_RGB;
|
||||
case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
|
||||
return CAIRO_SUBPIXEL_ORDER_BGR;
|
||||
case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
|
||||
return CAIRO_SUBPIXEL_ORDER_VRGB;
|
||||
case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
|
||||
return CAIRO_SUBPIXEL_ORDER_VBGR;
|
||||
default:
|
||||
return CAIRO_SUBPIXEL_ORDER_DEFAULT;
|
||||
}
|
||||
return CAIRO_SUBPIXEL_ORDER_DEFAULT;
|
||||
}
|
||||
|
||||
struct wlr_texture *
|
||||
create_message_texture(const char *string, const struct cg_output *output) {
|
||||
const int PADDING = 2;
|
||||
const int MESSAGE_HEIGHT = 17 + 2 * PADDING;
|
||||
const char *font = "pango:Monospace 10";
|
||||
|
||||
struct wlr_texture *texture;
|
||||
double scale = output->wlr_output->scale;
|
||||
int width = 0;
|
||||
int height = MESSAGE_HEIGHT * scale;
|
||||
|
||||
// We must use a non-nil cairo_t for cairo_set_font_options to work.
|
||||
// Therefore, we cannot use cairo_create(NULL).
|
||||
cairo_surface_t *dummy_surface =
|
||||
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
|
||||
cairo_t *c = cairo_create(dummy_surface);
|
||||
cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
|
||||
cairo_font_options_t *fo = cairo_font_options_create();
|
||||
cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
|
||||
cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
|
||||
cairo_font_options_set_subpixel_order(
|
||||
fo, to_cairo_subpixel_order(output->wlr_output->subpixel));
|
||||
cairo_set_font_options(c, fo);
|
||||
get_text_size(c, font, &width, NULL, NULL, scale, "%s", string);
|
||||
width += 2 * PADDING;
|
||||
cairo_surface_destroy(dummy_surface);
|
||||
cairo_destroy(c);
|
||||
|
||||
cairo_surface_t *surface =
|
||||
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
|
||||
cairo_t *cairo = cairo_create(surface);
|
||||
cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
|
||||
cairo_set_font_options(cairo, fo);
|
||||
cairo_font_options_destroy(fo);
|
||||
cairo_set_source_rgba(cairo, 0.9, 0.85, 0.85, 1.0);
|
||||
cairo_paint(cairo);
|
||||
PangoContext *pango = pango_cairo_create_context(cairo);
|
||||
cairo_set_source_rgba(cairo, 0, 0, 0, 1);
|
||||
cairo_set_line_width(cairo, 2);
|
||||
cairo_rectangle(cairo, 0, 0, width, height);
|
||||
cairo_stroke(cairo);
|
||||
cairo_move_to(cairo, PADDING, PADDING);
|
||||
|
||||
pango_printf(cairo, font, scale, "%s", string);
|
||||
|
||||
cairo_surface_flush(surface);
|
||||
unsigned char *data = cairo_image_surface_get_data(surface);
|
||||
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
|
||||
struct wlr_renderer *renderer =
|
||||
wlr_backend_get_renderer(output->wlr_output->backend);
|
||||
texture = wlr_texture_from_pixels(renderer, WL_SHM_FORMAT_ARGB8888, stride,
|
||||
width, height, data);
|
||||
cairo_surface_destroy(surface);
|
||||
g_object_unref(pango);
|
||||
cairo_destroy(cairo);
|
||||
return texture;
|
||||
}
|
||||
|
||||
void
|
||||
message_set_output(struct cg_output *output, const char *string,
|
||||
struct wlr_box *box, enum cg_message_align align) {
|
||||
struct cg_message *message = malloc(sizeof(struct cg_message));
|
||||
message->message = create_message_texture(string, output);
|
||||
message->position = box;
|
||||
wl_list_insert(&output->messages, &message->link);
|
||||
|
||||
int width, height;
|
||||
wlr_texture_get_size(message->message, &width, &height);
|
||||
message->position->width = width;
|
||||
message->position->height = height;
|
||||
switch(align) {
|
||||
case CG_MESSAGE_TOP_RIGHT: {
|
||||
message->position->x -= width;
|
||||
break;
|
||||
}
|
||||
case CG_MESSAGE_BOTTOM_LEFT: {
|
||||
message->position->y -= height;
|
||||
break;
|
||||
}
|
||||
case CG_MESSAGE_BOTTOM_RIGHT: {
|
||||
message->position->x -= width;
|
||||
message->position->y -= height;
|
||||
break;
|
||||
}
|
||||
case CG_MESSAGE_CENTER: {
|
||||
message->position->x -= width / 2;
|
||||
message->position->y -= height / 2;
|
||||
break;
|
||||
}
|
||||
case CG_MESSAGE_TOP_LEFT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
wlr_output_damage_add_box(output->damage, message->position);
|
||||
}
|
||||
|
||||
void
|
||||
message_printf(struct cg_output *output, const char *fmt, ...) {
|
||||
uint16_t buf_len = 256;
|
||||
char *buffer = (char *)malloc(buf_len * sizeof(char));
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buffer, buf_len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
struct wlr_box *box = malloc(sizeof(struct wlr_box));
|
||||
struct wlr_box *output_box = wlr_output_layout_get_box(
|
||||
output->server->output_layout, output->wlr_output);
|
||||
|
||||
box->x = output_box->width;
|
||||
box->y = 0;
|
||||
box->width = 0;
|
||||
box->height = 0;
|
||||
|
||||
message_set_output(output, buffer, box, CG_MESSAGE_TOP_RIGHT);
|
||||
free(buffer);
|
||||
alarm(output->server->message_timeout);
|
||||
}
|
||||
|
||||
void
|
||||
message_printf_pos(struct cg_output *output, struct wlr_box *position,
|
||||
const enum cg_message_align align, const char *fmt, ...) {
|
||||
uint16_t buf_len = 256;
|
||||
char *buffer = (char *)malloc(buf_len * sizeof(char));
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buffer, buf_len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
message_set_output(output, buffer, position, align);
|
||||
free(buffer);
|
||||
alarm(output->server->message_timeout);
|
||||
}
|
||||
|
||||
void
|
||||
message_clear(struct cg_output *output) {
|
||||
struct cg_message *message, *tmp;
|
||||
wl_list_for_each_safe(message, tmp, &output->messages, link) {
|
||||
wl_list_remove(&message->link);
|
||||
wlr_output_damage_add_box(output->damage, message->position);
|
||||
wlr_texture_destroy(message->message);
|
||||
free(message->position);
|
||||
free(message);
|
||||
}
|
||||
}
|
||||
33
message.h
Normal file
33
message.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef MESSAGE_H
|
||||
|
||||
#define MESSAGE_H
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
struct cg_output;
|
||||
struct wlr_box;
|
||||
struct wlr_texture;
|
||||
|
||||
enum cg_message_align {
|
||||
CG_MESSAGE_TOP_LEFT,
|
||||
CG_MESSAGE_TOP_RIGHT,
|
||||
CG_MESSAGE_BOTTOM_LEFT,
|
||||
CG_MESSAGE_BOTTOM_RIGHT,
|
||||
CG_MESSAGE_CENTER,
|
||||
};
|
||||
|
||||
struct cg_message {
|
||||
struct wlr_box *position;
|
||||
struct wlr_texture *message;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
void
|
||||
message_printf(struct cg_output *output, const char *fmt, ...);
|
||||
void
|
||||
message_printf_pos(struct cg_output *output, struct wlr_box *position,
|
||||
enum cg_message_align, const char *fmt, ...);
|
||||
void
|
||||
message_clear(struct cg_output *output);
|
||||
|
||||
#endif /* end of include guard MESSAGE_H */
|
||||
569
output.c
Normal file
569
output.c
Normal file
|
|
@ -0,0 +1,569 @@
|
|||
/*
|
||||
* Cagebreak: A Wayland tiling compositor.
|
||||
*
|
||||
* Copyright (C) 2020 The Cagebreak Authors
|
||||
* Copyright (C) 2018-2020 Jente Hidskes
|
||||
* Copyright (C) 2019 The Sway authors
|
||||
*
|
||||
* See the LICENSE file accompanying this file.
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#include "config.h"
|
||||
#include <wlr/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/backend/wayland.h>
|
||||
#if WLR_HAS_X11_BACKEND
|
||||
#include <wlr/backend/x11.h>
|
||||
#endif
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_data_device.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_output_damage.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_surface.h>
|
||||
#include <wlr/types/wlr_xcursor_manager.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <wlr/util/region.h>
|
||||
|
||||
#include "keybinding.h"
|
||||
#include "message.h"
|
||||
#include "output.h"
|
||||
#include "render.h"
|
||||
#include "seat.h"
|
||||
#include "server.h"
|
||||
#include "util.h"
|
||||
#include "view.h"
|
||||
#include "workspace.h"
|
||||
#if CG_HAS_XWAYLAND
|
||||
#include "xwayland.h"
|
||||
#endif
|
||||
|
||||
static void
|
||||
output_for_each_surface(struct cg_output *output,
|
||||
cg_surface_iterator_func_t iterator, void *user_data);
|
||||
|
||||
struct surface_iterator_data {
|
||||
cg_surface_iterator_func_t user_iterator;
|
||||
void *user_data;
|
||||
|
||||
struct cg_output *output;
|
||||
|
||||
/* Output-local coordinates. */
|
||||
double ox, oy;
|
||||
};
|
||||
|
||||
static bool
|
||||
intersects_with_output(struct cg_output *output,
|
||||
const struct wlr_output_layout *output_layout,
|
||||
const struct wlr_box *surface_box) {
|
||||
/* Since the surface_box's x- and y-coordinates are already output local,
|
||||
* the x- and y-coordinates of this box need to be 0 for this function to
|
||||
* work correctly. */
|
||||
struct wlr_box output_box = {0};
|
||||
wlr_output_effective_resolution(output->wlr_output, &output_box.width,
|
||||
&output_box.height);
|
||||
|
||||
struct wlr_box intersection;
|
||||
return wlr_box_intersection(&intersection, &output_box, surface_box);
|
||||
}
|
||||
|
||||
static void
|
||||
output_for_each_surface_iterator(struct wlr_surface *surface, int sx, int sy,
|
||||
void *user_data) {
|
||||
struct surface_iterator_data *data = user_data;
|
||||
struct cg_output *output = data->output;
|
||||
|
||||
if(!wlr_surface_has_buffer(surface)) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_box surface_box = {
|
||||
.x = data->ox + sx + surface->sx,
|
||||
.y = data->oy + sy + surface->sy,
|
||||
.width = surface->current.width,
|
||||
.height = surface->current.height,
|
||||
};
|
||||
|
||||
if(!intersects_with_output(output, output->server->output_layout,
|
||||
&surface_box)) {
|
||||
return;
|
||||
}
|
||||
|
||||
data->user_iterator(data->output, surface, &surface_box, data->user_data);
|
||||
}
|
||||
|
||||
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) {
|
||||
struct surface_iterator_data data = {
|
||||
.user_iterator = iterator,
|
||||
.user_data = user_data,
|
||||
.output = output,
|
||||
.ox = ox,
|
||||
.oy = oy,
|
||||
};
|
||||
|
||||
wlr_surface_for_each_surface(surface, output_for_each_surface_iterator,
|
||||
&data);
|
||||
}
|
||||
|
||||
static void
|
||||
output_view_for_each_surface(struct cg_output *output, struct cg_view *view,
|
||||
cg_surface_iterator_func_t iterator,
|
||||
void *user_data) {
|
||||
struct surface_iterator_data data = {
|
||||
.user_iterator = iterator,
|
||||
.user_data = user_data,
|
||||
.output = output,
|
||||
.ox = view->ox,
|
||||
.oy = view->oy,
|
||||
};
|
||||
|
||||
view_for_each_surface(view, output_for_each_surface_iterator, &data);
|
||||
}
|
||||
|
||||
void
|
||||
output_view_for_each_popup(struct cg_output *output, struct cg_view *view,
|
||||
cg_surface_iterator_func_t iterator,
|
||||
void *user_data) {
|
||||
struct surface_iterator_data data = {
|
||||
.user_iterator = iterator,
|
||||
.user_data = user_data,
|
||||
.output = output,
|
||||
.ox = view->ox,
|
||||
.oy = view->oy,
|
||||
};
|
||||
|
||||
view_for_each_popup(view, output_for_each_surface_iterator, &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) {
|
||||
struct cg_drag_icon *drag_icon;
|
||||
wl_list_for_each(drag_icon, drag_icons, link) {
|
||||
if(drag_icon->wlr_drag_icon->mapped) {
|
||||
double ox = drag_icon->lx;
|
||||
double oy = drag_icon->ly;
|
||||
wlr_output_layout_output_coords(output->server->output_layout,
|
||||
output->wlr_output, &ox, &oy);
|
||||
output_surface_for_each_surface(output,
|
||||
drag_icon->wlr_drag_icon->surface,
|
||||
ox, oy, iterator, user_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
output_for_each_surface(struct cg_output *output,
|
||||
cg_surface_iterator_func_t iterator, void *user_data) {
|
||||
struct cg_view *view;
|
||||
wl_list_for_each_reverse(
|
||||
view, &output->workspaces[output->curr_workspace]->views, link) {
|
||||
output_view_for_each_surface(output, view, iterator, user_data);
|
||||
}
|
||||
|
||||
wl_list_for_each_reverse(
|
||||
view, &output->workspaces[output->curr_workspace]->unmanaged_views,
|
||||
link) {
|
||||
output_view_for_each_surface(output, view, iterator, user_data);
|
||||
}
|
||||
|
||||
output_drag_icons_for_each_surface(
|
||||
output, &output->server->seat->drag_icons, iterator, user_data);
|
||||
}
|
||||
|
||||
struct send_frame_done_data {
|
||||
struct timespec when;
|
||||
};
|
||||
|
||||
static void
|
||||
send_frame_done_iterator(struct cg_output *output, struct wlr_surface *surface,
|
||||
struct wlr_box *box, void *user_data) {
|
||||
struct send_frame_done_data *data = user_data;
|
||||
wlr_surface_send_frame_done(surface, &data->when);
|
||||
}
|
||||
|
||||
static void
|
||||
send_frame_done(struct cg_output *output, struct send_frame_done_data *data) {
|
||||
output_for_each_surface(output, send_frame_done_iterator, data);
|
||||
}
|
||||
|
||||
struct damage_data {
|
||||
bool whole;
|
||||
};
|
||||
|
||||
static void
|
||||
count_surface_iterator(struct cg_output *output, struct wlr_surface *surface,
|
||||
struct wlr_box *_box, void *data) {
|
||||
size_t *n = data;
|
||||
n++;
|
||||
}
|
||||
|
||||
static bool
|
||||
scan_out_primary_view(struct cg_output *output) {
|
||||
struct cg_server *server = output->server;
|
||||
struct wlr_output *wlr_output = output->wlr_output;
|
||||
|
||||
if(!wl_list_empty(&output->messages)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct cg_drag_icon *drag_icon;
|
||||
wl_list_for_each(drag_icon, &server->seat->drag_icons, link) {
|
||||
if(drag_icon->wlr_drag_icon->mapped) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct cg_view *view = seat_get_focus(server->seat);
|
||||
if(view == NULL || view->wlr_surface == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t n_surfaces = 0;
|
||||
output_view_for_each_surface(output, view, count_surface_iterator,
|
||||
&n_surfaces);
|
||||
if(n_surfaces > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if CG_HAS_XWAYLAND
|
||||
if(view->type == CG_XWAYLAND_VIEW) {
|
||||
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
||||
if(!wl_list_empty(&xwayland_view->xwayland_surface->children) ||
|
||||
!wl_list_empty(&view->workspace->unmanaged_views)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct wlr_surface *surface = view->wlr_surface;
|
||||
|
||||
if(!surface->buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if((float)surface->current.scale != wlr_output->scale ||
|
||||
surface->current.transform != wlr_output->transform) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!wlr_output_attach_buffer(wlr_output, surface->buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return wlr_output_commit(wlr_output);
|
||||
}
|
||||
|
||||
static void
|
||||
damage_surface_iterator(struct cg_output *output, struct wlr_surface *surface,
|
||||
struct wlr_box *box, void *user_data) {
|
||||
struct wlr_output *wlr_output = output->wlr_output;
|
||||
struct damage_data *data = (struct damage_data *)user_data;
|
||||
bool whole = data->whole;
|
||||
|
||||
scale_box(box, output->wlr_output->scale);
|
||||
|
||||
if(whole) {
|
||||
wlr_output_damage_add_box(output->damage, box);
|
||||
} else if(pixman_region32_not_empty(&surface->buffer_damage)) {
|
||||
pixman_region32_t damage;
|
||||
pixman_region32_init(&damage);
|
||||
wlr_surface_get_effective_damage(surface, &damage);
|
||||
|
||||
if(ceil(wlr_output->scale) > surface->current.scale) {
|
||||
/* When scaling up a surface it'll become
|
||||
blurry, so we need to expand the damage
|
||||
region. */
|
||||
wlr_region_expand(&damage, &damage,
|
||||
ceil(wlr_output->scale) - surface->current.scale);
|
||||
}
|
||||
pixman_region32_translate(&damage, box->x, box->y);
|
||||
wlr_output_damage_add(output->damage, &damage);
|
||||
pixman_region32_fini(&damage);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
output_damage_surface(struct cg_output *output, struct wlr_surface *surface,
|
||||
double ox, double oy, bool whole) {
|
||||
struct damage_data data = {
|
||||
.whole = whole,
|
||||
};
|
||||
|
||||
output_surface_for_each_surface(output, surface, ox, oy,
|
||||
damage_surface_iterator, &data);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_output_damage_frame(struct wl_listener *listener, void *data) {
|
||||
struct cg_output *output = wl_container_of(listener, output, damage_frame);
|
||||
struct send_frame_done_data frame_data = {0};
|
||||
|
||||
if(!output->wlr_output->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if we can scan-out the primary view. */
|
||||
static bool last_scanned_out = false;
|
||||
bool scanned_out = scan_out_primary_view(output);
|
||||
|
||||
if(scanned_out && !last_scanned_out) {
|
||||
wlr_log(WLR_DEBUG, "Scanning out primary view");
|
||||
}
|
||||
if(last_scanned_out && !scanned_out) {
|
||||
wlr_log(WLR_DEBUG, "Stopping primary view scan out");
|
||||
if(seat_get_focus(output->server->seat) != NULL) {
|
||||
view_damage_whole(seat_get_focus(output->server->seat));
|
||||
}
|
||||
}
|
||||
last_scanned_out = scanned_out;
|
||||
|
||||
if(scanned_out) {
|
||||
goto frame_done;
|
||||
}
|
||||
|
||||
bool needs_frame;
|
||||
pixman_region32_t damage;
|
||||
pixman_region32_init(&damage);
|
||||
if(!wlr_output_damage_attach_render(output->damage, &needs_frame,
|
||||
&damage)) {
|
||||
wlr_log(WLR_ERROR, "Cannot make damage output current");
|
||||
goto damage_finish;
|
||||
}
|
||||
|
||||
if(!needs_frame) {
|
||||
wlr_output_rollback(output->wlr_output);
|
||||
goto damage_finish;
|
||||
}
|
||||
|
||||
output_render(output, &damage);
|
||||
|
||||
damage_finish:
|
||||
pixman_region32_fini(&damage);
|
||||
|
||||
frame_done:
|
||||
clock_gettime(CLOCK_MONOTONIC, &frame_data.when);
|
||||
send_frame_done(output, &frame_data);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_output_transform(struct wl_listener *listener, void *data) {
|
||||
struct cg_output *output = wl_container_of(listener, output, transform);
|
||||
|
||||
if(!output->wlr_output->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct cg_view *view;
|
||||
wl_list_for_each(view, &output->workspaces[output->curr_workspace]->views,
|
||||
link) {
|
||||
view_position(view);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_output_mode(struct wl_listener *listener, void *data) {
|
||||
struct cg_output *output = wl_container_of(listener, output, mode);
|
||||
|
||||
if(!output->wlr_output->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct cg_view *view;
|
||||
wl_list_for_each(view, &output->workspaces[output->curr_workspace]->views,
|
||||
link) {
|
||||
view_position(view);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
output_destroy(struct cg_output *output) {
|
||||
struct cg_server *server = output->server;
|
||||
|
||||
wl_list_remove(&output->destroy.link);
|
||||
wl_list_remove(&output->mode.link);
|
||||
wl_list_remove(&output->transform.link);
|
||||
wl_list_remove(&output->damage_frame.link);
|
||||
wl_list_remove(&output->damage_destroy.link);
|
||||
|
||||
wlr_output_layout_remove(server->output_layout, output->wlr_output);
|
||||
|
||||
if(server->running && server->curr_output == output &&
|
||||
wl_list_length(&server->outputs) > 1) {
|
||||
keybinding_cycle_outputs(server, false);
|
||||
}
|
||||
wl_list_remove(&output->link);
|
||||
|
||||
message_clear(output);
|
||||
|
||||
struct cg_view *view, *view_tmp;
|
||||
if(server->running) {
|
||||
for(unsigned int i = 0; i < server->nws; ++i) {
|
||||
wl_list_for_each_safe(view, view_tmp, &output->workspaces[i]->views,
|
||||
link) {
|
||||
wl_list_remove(&view->link);
|
||||
if(wl_list_empty(&server->outputs)) {
|
||||
view->impl->destroy(view);
|
||||
} else {
|
||||
wl_list_insert(
|
||||
&server->curr_output
|
||||
->workspaces[server->curr_output->curr_workspace]
|
||||
->views,
|
||||
&view->link);
|
||||
view->workspace =
|
||||
server->curr_output
|
||||
->workspaces[server->curr_output->curr_workspace];
|
||||
if(server->seat->focused_view == NULL) {
|
||||
seat_set_focus(server->seat, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
wl_list_for_each_safe(
|
||||
view, view_tmp, &output->workspaces[i]->unmanaged_views, link) {
|
||||
wl_list_remove(&view->link);
|
||||
if(wl_list_empty(&server->outputs)) {
|
||||
view->impl->destroy(view);
|
||||
} else {
|
||||
wl_list_insert(
|
||||
&server->curr_output
|
||||
->workspaces[server->curr_output->curr_workspace]
|
||||
->views,
|
||||
&view->link);
|
||||
view->workspace =
|
||||
server->curr_output
|
||||
->workspaces[server->curr_output->curr_workspace];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Important: due to unfortunate events, "workspace" and "output->workspace"
|
||||
* have nothing in common. The former is the workspace of a single output,
|
||||
* whereas the latter is the workspace of the outputs.*/
|
||||
for(unsigned int i = 0; i < server->nws; ++i) {
|
||||
workspace_free(output->workspaces[i]);
|
||||
}
|
||||
free(output->workspaces);
|
||||
|
||||
free(output);
|
||||
|
||||
if(wl_list_empty(&server->outputs)) {
|
||||
wl_display_terminate(server->wl_display);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_output_damage_destroy(struct wl_listener *listener, void *data) {
|
||||
struct cg_output *output =
|
||||
wl_container_of(listener, output, damage_destroy);
|
||||
output_destroy(output);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_output_destroy(struct wl_listener *listener, void *data) {
|
||||
struct cg_output *output = wl_container_of(listener, output, destroy);
|
||||
wlr_output_damage_destroy(output->damage);
|
||||
output_destroy(output);
|
||||
}
|
||||
|
||||
void
|
||||
handle_new_output(struct wl_listener *listener, void *data) {
|
||||
struct cg_server *server = wl_container_of(listener, server, new_output);
|
||||
struct wlr_output *wlr_output = data;
|
||||
|
||||
struct cg_output *output = calloc(1, sizeof(struct cg_output));
|
||||
if(!output) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate output");
|
||||
return;
|
||||
}
|
||||
|
||||
output->wlr_output = wlr_output;
|
||||
output->server = server;
|
||||
output->damage = wlr_output_damage_create(wlr_output);
|
||||
wl_list_insert(&server->outputs, &output->link);
|
||||
|
||||
output->mode.notify = handle_output_mode;
|
||||
wl_signal_add(&wlr_output->events.mode, &output->mode);
|
||||
output->transform.notify = handle_output_transform;
|
||||
wl_signal_add(&wlr_output->events.transform, &output->transform);
|
||||
output->destroy.notify = handle_output_destroy;
|
||||
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
|
||||
output->damage_frame.notify = handle_output_damage_frame;
|
||||
wl_signal_add(&output->damage->events.frame, &output->damage_frame);
|
||||
output->damage_destroy.notify = handle_output_damage_destroy;
|
||||
wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
|
||||
|
||||
wlr_output_set_transform(wlr_output, server->output_transform);
|
||||
wlr_output_layout_add_auto(server->output_layout, wlr_output);
|
||||
|
||||
output->workspaces = malloc(server->nws * sizeof(struct cg_workspace *));
|
||||
for(unsigned int i = 0; i < server->nws; ++i) {
|
||||
output->workspaces[i] = full_screen_workspace(output);
|
||||
wl_list_init(&output->workspaces[i]->views);
|
||||
wl_list_init(&output->workspaces[i]->unmanaged_views);
|
||||
}
|
||||
|
||||
output->curr_workspace = 0;
|
||||
wl_list_init(&output->messages);
|
||||
|
||||
struct wlr_output_mode *preferred_mode =
|
||||
wlr_output_preferred_mode(wlr_output);
|
||||
if(preferred_mode) {
|
||||
wlr_output_set_mode(wlr_output, preferred_mode);
|
||||
}
|
||||
|
||||
if(wlr_xcursor_manager_load(server->seat->xcursor_manager,
|
||||
wlr_output->scale)) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Cannot load XCursor theme for output '%s' with scale %f",
|
||||
wlr_output->name, wlr_output->scale);
|
||||
}
|
||||
|
||||
wlr_output_damage_add_whole(output->damage);
|
||||
|
||||
wlr_output_enable(wlr_output, true);
|
||||
wlr_output_commit(wlr_output);
|
||||
|
||||
/* TODO:If you see a way to circumvent having to loop twice, please do so */
|
||||
for(unsigned int i = 0; i < server->nws; ++i) {
|
||||
workspace_free(output->workspaces[i]);
|
||||
output->workspaces[i] = full_screen_workspace(output);
|
||||
wl_list_init(&output->workspaces[i]->views);
|
||||
wl_list_init(&output->workspaces[i]->unmanaged_views);
|
||||
}
|
||||
|
||||
/* We are the first output. Set the current output to this one. */
|
||||
if(server->curr_output == NULL) {
|
||||
server->curr_output = output;
|
||||
}
|
||||
wlr_xcursor_manager_set_cursor_image(server->seat->xcursor_manager,
|
||||
DEFAULT_XCURSOR, server->seat->cursor);
|
||||
wlr_cursor_warp(server->seat->cursor, NULL, 0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
output_set_window_title(struct cg_output *output, const char *title) {
|
||||
struct wlr_output *wlr_output = output->wlr_output;
|
||||
|
||||
if(wlr_output_is_wl(wlr_output)) {
|
||||
wlr_wl_output_set_title(wlr_output, title);
|
||||
#if WLR_HAS_X11_BACKEND
|
||||
} else if(wlr_output_is_x11(wlr_output)) {
|
||||
wlr_x11_output_set_title(wlr_output, title);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
57
output.h
Normal file
57
output.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef CG_OUTPUT_H
|
||||
#define CG_OUTPUT_H
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
struct cg_server;
|
||||
struct cg_view;
|
||||
struct wlr_output;
|
||||
struct wlr_output_damage;
|
||||
struct wlr_surface;
|
||||
struct wlr_box;
|
||||
|
||||
struct cg_output {
|
||||
struct cg_server *server;
|
||||
struct wlr_output *wlr_output;
|
||||
struct wlr_output_damage *damage;
|
||||
|
||||
struct wl_listener mode;
|
||||
struct wl_listener transform;
|
||||
struct wl_listener destroy;
|
||||
struct wl_listener damage_frame;
|
||||
struct wl_listener damage_destroy;
|
||||
struct cg_workspace **workspaces;
|
||||
struct wl_list messages;
|
||||
int curr_workspace;
|
||||
|
||||
struct wl_list link; // cg_server::outputs
|
||||
};
|
||||
|
||||
typedef void (*cg_surface_iterator_func_t)(struct cg_output *output,
|
||||
struct wlr_surface *surface,
|
||||
struct wlr_box *box,
|
||||
void *user_data);
|
||||
|
||||
void
|
||||
handle_new_output(struct wl_listener *listener, void *data);
|
||||
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);
|
||||
|
||||
#endif
|
||||
94
pango.c
Normal file
94
pango.c
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#include "cairo.h"
|
||||
#include <cairo/cairo.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
char *
|
||||
lenient_strcat(char *dest, const char *src) {
|
||||
if(dest && src) {
|
||||
return strcat(dest, src);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
PangoLayout *
|
||||
get_pango_layout(cairo_t *cairo, const char *font, const char *text,
|
||||
double scale) {
|
||||
PangoLayout *layout = pango_cairo_create_layout(cairo);
|
||||
PangoAttrList *attrs;
|
||||
|
||||
attrs = pango_attr_list_new();
|
||||
pango_layout_set_text(layout, text, -1);
|
||||
|
||||
pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
|
||||
PangoFontDescription *desc = pango_font_description_from_string(font);
|
||||
pango_layout_set_font_description(layout, desc);
|
||||
pango_layout_set_single_paragraph_mode(layout, 1);
|
||||
pango_layout_set_attributes(layout, attrs);
|
||||
pango_attr_list_unref(attrs);
|
||||
pango_font_description_free(desc);
|
||||
return layout;
|
||||
}
|
||||
|
||||
void
|
||||
get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
|
||||
int *baseline, double scale, const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
// Add one since vsnprintf excludes null terminator.
|
||||
int length = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
char *buf = malloc(length);
|
||||
if(buf == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, length, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale);
|
||||
pango_cairo_update_layout(cairo, layout);
|
||||
pango_layout_get_pixel_size(layout, width, height);
|
||||
if(baseline) {
|
||||
*baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
|
||||
}
|
||||
g_object_unref(layout);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void
|
||||
pango_printf(cairo_t *cairo, const char *font, double scale, const char *fmt,
|
||||
...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
// Add one since vsnprintf excludes null terminator.
|
||||
int length = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
char *buf = malloc(length);
|
||||
if(buf == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, length, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale);
|
||||
cairo_font_options_t *fo = cairo_font_options_create();
|
||||
cairo_get_font_options(cairo, fo);
|
||||
pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo);
|
||||
cairo_font_options_destroy(fo);
|
||||
pango_cairo_update_layout(cairo, layout);
|
||||
pango_cairo_show_layout(cairo, layout);
|
||||
g_object_unref(layout);
|
||||
free(buf);
|
||||
}
|
||||
13
pango.h
Normal file
13
pango.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef _SWAY_PANGO_H
|
||||
#define _SWAY_PANGO_H
|
||||
#include <cairo/cairo.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void
|
||||
get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
|
||||
int *baseline, double scale, const char *fmt, ...);
|
||||
void
|
||||
pango_printf(cairo_t *cairo, const char *font, double scale, const char *fmt,
|
||||
...);
|
||||
|
||||
#endif
|
||||
416
parse.c
Normal file
416
parse.c
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
#define _POSIX_C_SOURCE 200812L
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
#include "keybinding.h"
|
||||
#include "output.h"
|
||||
#include "parse.h"
|
||||
#include "server.h"
|
||||
#include "workspace.h"
|
||||
|
||||
int
|
||||
parse_action(struct cg_server *server, struct keybinding *keybinding,
|
||||
char **saveptr) {
|
||||
char *action = strtok_r(NULL, " ", saveptr);
|
||||
if(action == NULL) {
|
||||
wlr_log(
|
||||
WLR_ERROR,
|
||||
"Not enough parameters to \"bind\". Expected action to execute");
|
||||
return -1;
|
||||
}
|
||||
keybinding->data = (union keybinding_params){.c = NULL};
|
||||
if(strcmp(action, "vsplit") == 0) {
|
||||
keybinding->action = KEYBINDING_SPLIT_VERTICAL;
|
||||
} else if(strcmp(action, "hsplit") == 0) {
|
||||
keybinding->action = KEYBINDING_SPLIT_HORIZONTAL;
|
||||
} else if(strcmp(action, "quit") == 0) {
|
||||
keybinding->action = KEYBINDING_QUIT;
|
||||
} else if(strcmp(action, "focus") == 0) {
|
||||
keybinding->action = KEYBINDING_CYCLE_TILES;
|
||||
keybinding->data.b = false;
|
||||
} else if(strcmp(action, "focusprev") == 0) {
|
||||
keybinding->action = KEYBINDING_CYCLE_TILES;
|
||||
keybinding->data.b = true;
|
||||
} else if(strcmp(action, "next") == 0) {
|
||||
keybinding->action = KEYBINDING_CYCLE_VIEWS;
|
||||
keybinding->data.b = false;
|
||||
} else if(strcmp(action, "prev") == 0) {
|
||||
keybinding->action = KEYBINDING_CYCLE_VIEWS;
|
||||
keybinding->data.b = true;
|
||||
} else if(strcmp(action, "only") == 0) {
|
||||
keybinding->action = KEYBINDING_LAYOUT_FULLSCREEN;
|
||||
} else if(strcmp(action, "abort") == 0) {
|
||||
keybinding->action = KEYBINDING_NOOP;
|
||||
} else if(strcmp(action, "time") == 0) {
|
||||
keybinding->action = KEYBINDING_SHOW_TIME;
|
||||
} else if(strcmp(action, "nextscreen") == 0) {
|
||||
keybinding->action = KEYBINDING_CYCLE_OUTPUT;
|
||||
keybinding->data.b = false;
|
||||
} else if(strcmp(action, "prevscreen") == 0) {
|
||||
keybinding->action = KEYBINDING_CYCLE_OUTPUT;
|
||||
keybinding->data.b = true;
|
||||
} else if(strcmp(action, "exec") == 0) {
|
||||
keybinding->action = KEYBINDING_RUN_COMMAND;
|
||||
if(*saveptr == NULL) {
|
||||
wlr_log(WLR_ERROR, "Not enough paramaters to \"exec\". Expected "
|
||||
"string to execute.");
|
||||
return -1;
|
||||
}
|
||||
keybinding->data.c = strdup(*saveptr);
|
||||
} else if(strcmp(action, "resizeleft") == 0) {
|
||||
keybinding->action = KEYBINDING_RESIZE_TILE_HORIZONTAL;
|
||||
keybinding->data.i = -10;
|
||||
} else if(strcmp(action, "resizeright") == 0) {
|
||||
keybinding->action = KEYBINDING_RESIZE_TILE_HORIZONTAL;
|
||||
keybinding->data.i = 10;
|
||||
} else if(strcmp(action, "resizedown") == 0) {
|
||||
keybinding->action = KEYBINDING_RESIZE_TILE_VERTICAL;
|
||||
keybinding->data.i = 10;
|
||||
} else if(strcmp(action, "resizeup") == 0) {
|
||||
keybinding->action = KEYBINDING_RESIZE_TILE_VERTICAL;
|
||||
keybinding->data.i = -10;
|
||||
} else if(strcmp(action, "workspace") == 0) {
|
||||
keybinding->action = KEYBINDING_SWITCH_WORKSPACE;
|
||||
char *nws_str = strtok_r(NULL, " ", saveptr);
|
||||
if(nws_str == NULL) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Expected argument for \"workspace\" action, got none.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
long ws = strtol(nws_str, NULL, 10);
|
||||
if(!(1 <= ws && ws <= server->nws)) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Requested binding for workspace %li, but have %u", ws,
|
||||
server->nws);
|
||||
return -1;
|
||||
}
|
||||
keybinding->data.u = ws - 1;
|
||||
} else if(strcmp(action, "movetoworkspace") == 0) {
|
||||
keybinding->action = KEYBINDING_MOVE_VIEW_TO_WORKSPACE;
|
||||
char *nws_str = strtok_r(NULL, " ", saveptr);
|
||||
if(nws_str == NULL) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Expected argument for \"workspace\" action, got none.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
long ws = strtol(nws_str, NULL, 10);
|
||||
if(!(1 <= ws && ws <= server->nws)) {
|
||||
wlr_log(
|
||||
WLR_ERROR,
|
||||
"Requested binding for moving to workspace %li, but have %u",
|
||||
ws, server->nws);
|
||||
return -1;
|
||||
}
|
||||
keybinding->data.u = ws - 1;
|
||||
} else if(strcmp(action, "exchangeleft") == 0) {
|
||||
keybinding->action = KEYBINDING_SWAP_LEFT;
|
||||
} else if(strcmp(action, "exchangeright") == 0) {
|
||||
keybinding->action = KEYBINDING_SWAP_RIGHT;
|
||||
} else if(strcmp(action, "exchangeup") == 0) {
|
||||
keybinding->action = KEYBINDING_SWAP_TOP;
|
||||
} else if(strcmp(action, "exchangedown") == 0) {
|
||||
keybinding->action = KEYBINDING_SWAP_BOTTOM;
|
||||
} else if(strcmp(action, "focusleft") == 0) {
|
||||
keybinding->action = KEYBINDING_FOCUS_LEFT;
|
||||
} else if(strcmp(action, "focusright") == 0) {
|
||||
keybinding->action = KEYBINDING_FOCUS_RIGHT;
|
||||
} else if(strcmp(action, "focusup") == 0) {
|
||||
keybinding->action = KEYBINDING_FOCUS_TOP;
|
||||
} else if(strcmp(action, "focusdown") == 0) {
|
||||
keybinding->action = KEYBINDING_FOCUS_BOTTOM;
|
||||
} else if(strcmp(action, "movetonextscreen") == 0) {
|
||||
keybinding->action = KEYBINDING_MOVE_VIEW_TO_NEXT_OUTPUT;
|
||||
} else if(strcmp(action, "switchvt") == 0) {
|
||||
keybinding->action = KEYBINDING_CHANGE_TTY;
|
||||
char *ntty = strtok_r(NULL, " ", saveptr);
|
||||
if(ntty == NULL) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Expected argument for \"switchvt\" command, got none.");
|
||||
return -1;
|
||||
}
|
||||
long tty = strtol(ntty, NULL, 10);
|
||||
keybinding->data.u = tty;
|
||||
} else if(strcmp(action, "mode") == 0) {
|
||||
keybinding->action = KEYBINDING_SWITCH_MODE;
|
||||
char *mode = strtok_r(NULL, " ", saveptr);
|
||||
if(mode == NULL) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Expected mode after \"switch_mode\". Got nothing.");
|
||||
return -1;
|
||||
}
|
||||
int mode_idx = get_mode_index_from_name(server->modes, mode);
|
||||
if(mode_idx == -1) {
|
||||
wlr_log(WLR_ERROR, "Unknown mode \"%s\" for switch_mode", mode);
|
||||
return -1;
|
||||
}
|
||||
keybinding->data.u = (unsigned int)mode_idx;
|
||||
} else if(strcmp(action, "setmode") == 0) {
|
||||
keybinding->action = KEYBINDING_SWITCH_DEFAULT_MODE;
|
||||
char *mode = strtok_r(NULL, " ", saveptr);
|
||||
if(mode == NULL) {
|
||||
wlr_log(
|
||||
WLR_ERROR,
|
||||
"Expected mode after \"switch_default_mode\". Got nothing.");
|
||||
return -1;
|
||||
}
|
||||
int mode_idx = get_mode_index_from_name(server->modes, mode);
|
||||
if(mode_idx == -1) {
|
||||
wlr_log(WLR_ERROR, "Unknown mode \"%s\" for switch_default_mode",
|
||||
mode);
|
||||
return -1;
|
||||
}
|
||||
keybinding->data.u = (unsigned int)mode_idx;
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "Error, unsupported action \"%s\".", action);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parses a key definition (e.g. "S-Tab") and sets key and modifiers in
|
||||
* keybinding respectivly */
|
||||
int
|
||||
parse_key(struct keybinding *keybinding, const char *key_def) {
|
||||
if(key_def == NULL) {
|
||||
wlr_log(WLR_ERROR, "Expected key definition, got nothing.");
|
||||
return -1;
|
||||
}
|
||||
keybinding->modifiers = 0;
|
||||
while(strlen(key_def) > 1 && key_def[1] == '-') {
|
||||
switch(key_def[0]) {
|
||||
case 'S':
|
||||
keybinding->modifiers |= WLR_MODIFIER_SHIFT;
|
||||
break;
|
||||
case 'A':
|
||||
keybinding->modifiers |= WLR_MODIFIER_ALT;
|
||||
break;
|
||||
case 'C':
|
||||
keybinding->modifiers |= WLR_MODIFIER_CTRL;
|
||||
break;
|
||||
case 'L':
|
||||
keybinding->modifiers |= WLR_MODIFIER_LOGO;
|
||||
break;
|
||||
case '2':
|
||||
keybinding->modifiers |= WLR_MODIFIER_MOD2;
|
||||
break;
|
||||
case '3':
|
||||
keybinding->modifiers |= WLR_MODIFIER_MOD3;
|
||||
break;
|
||||
case '5':
|
||||
keybinding->modifiers |= WLR_MODIFIER_MOD5;
|
||||
break;
|
||||
default:
|
||||
wlr_log(WLR_ERROR, "Unknown modifier \"%c\"", key_def[0]);
|
||||
return -1;
|
||||
}
|
||||
key_def += 2;
|
||||
}
|
||||
xkb_keysym_t keysym = xkb_keysym_from_name(key_def, XKB_KEYSYM_NO_FLAGS);
|
||||
if(keysym == XKB_KEY_NoSymbol) {
|
||||
wlr_log(WLR_ERROR, "Could not convert key \"%s\" to keysym.", key_def);
|
||||
return -1;
|
||||
}
|
||||
keybinding->key = keysym;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse a keybinding definition and return it if successful, else return NULL
|
||||
*/
|
||||
struct keybinding *
|
||||
parse_keybinding(struct cg_server *server, char **saveptr) {
|
||||
struct keybinding *keybinding = malloc(sizeof(struct keybinding));
|
||||
char *key = strtok_r(NULL, " ", saveptr);
|
||||
if(parse_key(keybinding, key) != 0) {
|
||||
wlr_log(WLR_ERROR, "Could not parse key definition \"%s\"", key);
|
||||
free(keybinding);
|
||||
return NULL;
|
||||
}
|
||||
if(parse_action(server, keybinding, saveptr) != 0) {
|
||||
free(keybinding);
|
||||
return NULL;
|
||||
}
|
||||
return keybinding;
|
||||
}
|
||||
|
||||
int
|
||||
parse_bind(struct cg_server *server, struct keybinding_list *list,
|
||||
char **saveptr) {
|
||||
struct keybinding *keybinding = parse_keybinding(server, saveptr);
|
||||
if(keybinding == NULL) {
|
||||
wlr_log(WLR_ERROR, "Could not parse keybinding for \"bind\".");
|
||||
return -1;
|
||||
}
|
||||
keybinding->mode = 1;
|
||||
keybinding_list_push(list, keybinding);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
parse_definekey(struct cg_server *server, struct keybinding_list *list,
|
||||
char **saveptr) {
|
||||
char *mode = strtok_r(NULL, " ", saveptr);
|
||||
if(mode == NULL) {
|
||||
wlr_log(WLR_ERROR, "Too few arguments to \"definekey\". Expected mode");
|
||||
return -1;
|
||||
}
|
||||
int mode_idx = get_mode_index_from_name(server->modes, mode);
|
||||
if(mode_idx == -1) {
|
||||
wlr_log(WLR_ERROR, "Unknown mode \"%s\"", mode);
|
||||
return -1;
|
||||
}
|
||||
struct keybinding *keybinding = parse_keybinding(server, saveptr);
|
||||
if(keybinding == NULL) {
|
||||
wlr_log(WLR_ERROR, "Could not parse keybinding for \"definekey\"");
|
||||
return -1;
|
||||
}
|
||||
keybinding->mode = mode_idx;
|
||||
keybinding_list_push(list, keybinding);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
parse_and_run_exec(char **saveptr) {
|
||||
return run_action(KEYBINDING_RUN_COMMAND, NULL,
|
||||
(union keybinding_params){.c = *saveptr});
|
||||
}
|
||||
|
||||
int
|
||||
parse_background(struct cg_server *server, char **saveptr) {
|
||||
/* Read rgb numbers */
|
||||
for(unsigned int i = 0; i < 3; ++i) {
|
||||
char *nstr = strtok_r(NULL, " ", saveptr);
|
||||
if(nstr == NULL) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Expected three space-separated numbers (rgb) for "
|
||||
"background color setting. Got %d.",
|
||||
i);
|
||||
return -1;
|
||||
}
|
||||
char *endptr = NULL;
|
||||
float nval = strtof(nstr, &endptr);
|
||||
if(endptr == nstr) {
|
||||
wlr_log(
|
||||
WLR_ERROR,
|
||||
"Could not parse number \"%s\" für background color setting.",
|
||||
nstr);
|
||||
return -1;
|
||||
}
|
||||
server->bg_color[i] = nval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
parse_escape(struct keybinding_list *list, char **saveptr) {
|
||||
struct keybinding *keybinding = malloc(sizeof(struct keybinding));
|
||||
char *key = strtok_r(NULL, " ", saveptr);
|
||||
if(parse_key(keybinding, key) != 0) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Could not parse key definition \"%s\" for \"escape\"", key);
|
||||
free(keybinding);
|
||||
return -1;
|
||||
}
|
||||
keybinding->mode = 0; //"top" mode
|
||||
keybinding->action = KEYBINDING_SWITCH_MODE;
|
||||
keybinding->data.u = 1; //"root" mode
|
||||
keybinding_list_push(list, keybinding);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
parse_definemode(char ***modes, char **saveptr) {
|
||||
int length = 0;
|
||||
while((*modes)[length++] != NULL)
|
||||
;
|
||||
char **tmp = realloc(*modes, (length + 1) * sizeof(char *));
|
||||
if(tmp == NULL) {
|
||||
wlr_log(WLR_ERROR, "Could not allocate memory for storing modes.");
|
||||
return -1;
|
||||
}
|
||||
*modes = tmp;
|
||||
(*modes)[length] = NULL;
|
||||
|
||||
char *mode = strtok_r(NULL, " ", saveptr);
|
||||
if(mode == NULL) {
|
||||
wlr_log(WLR_ERROR, "Expected mode to succeed \"definemode\" keyword.");
|
||||
return -1;
|
||||
}
|
||||
(*modes)[length - 1] = strdup(mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
parse_rc_line(struct cg_server *server, char *line) {
|
||||
char *saveptr = NULL; // Used internally by strtok_r
|
||||
char *command = strtok_r(line, " ", &saveptr);
|
||||
if(command == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(command, "bind") == 0) {
|
||||
if(parse_bind(server, server->keybindings, &saveptr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if(strcmp(command, "definekey") == 0) {
|
||||
if(parse_definekey(server, server->keybindings, &saveptr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if(strcmp(command, "background") == 0) {
|
||||
if(parse_background(server, &saveptr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if(strcmp(command, "escape") == 0) {
|
||||
if(parse_escape(server->keybindings, &saveptr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if(strcmp(command, "definemode") == 0) {
|
||||
if(parse_definemode(&server->modes, &saveptr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if(strcmp(command, "exec") == 0) {
|
||||
if(parse_and_run_exec(&saveptr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if(strcmp(command, "workspaces") == 0) {
|
||||
char *nws_str = strtok_r(NULL, " ", &saveptr);
|
||||
if(nws_str == NULL) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Expected argument for \"workspaces\" command, got none.");
|
||||
return -1;
|
||||
}
|
||||
long nws = strtol(nws_str, NULL, 10);
|
||||
if(!(1 <= nws && nws <= 30)) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"More than 30 workspaces are not supported. Received %li",
|
||||
nws);
|
||||
return -1;
|
||||
}
|
||||
struct cg_output *output;
|
||||
wl_list_for_each(output, &server->outputs, link) {
|
||||
for(unsigned int i = nws; i < server->nws; ++i) {
|
||||
free(output->workspaces[i]);
|
||||
}
|
||||
struct cg_workspace **new_workspaces = realloc(
|
||||
output->workspaces, nws * sizeof(struct cg_workspace *));
|
||||
if(new_workspaces == NULL) {
|
||||
wlr_log(WLR_ERROR, "Error reallocating memory for workspaces.");
|
||||
return -1;
|
||||
}
|
||||
output->workspaces = new_workspaces;
|
||||
for(unsigned int i = server->nws; i < nws; ++i) {
|
||||
output->workspaces[i] = full_screen_workspace(output);
|
||||
wl_list_init(&output->workspaces[i]->views);
|
||||
wl_list_init(&output->workspaces[i]->unmanaged_views);
|
||||
}
|
||||
}
|
||||
server->nws = nws;
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "Unsupported command \"%s\" in config file",
|
||||
command);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
10
parse.h
Normal file
10
parse.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef PARSE_H
|
||||
|
||||
#define PARSE_H
|
||||
|
||||
struct cg_server;
|
||||
|
||||
int
|
||||
parse_rc_line(struct cg_server *server, char *line);
|
||||
|
||||
#endif /* end of include guard PARSE_H */
|
||||
254
render.c
Normal file
254
render.c
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Cagebreak: A Wayland tiling compositor.
|
||||
*
|
||||
* Copyright (C) 2020 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_box.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_output_damage.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_surface.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 = wlr_backend_get_renderer(output->backend);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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 =
|
||||
wlr_backend_get_renderer(wlr_output->backend);
|
||||
|
||||
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]);
|
||||
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);
|
||||
|
||||
render_texture(wlr_output, output_damage, texture, box, matrix);
|
||||
}
|
||||
|
||||
static void
|
||||
render_drag_icons(struct cg_output *output, pixman_region32_t *damage,
|
||||
struct wl_list *drag_icons) {
|
||||
struct render_data data = {
|
||||
.damage = damage,
|
||||
};
|
||||
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,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
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 =
|
||||
wlr_backend_get_renderer(wlr_output->backend);
|
||||
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]);
|
||||
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);
|
||||
}
|
||||
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
Normal file
9
render.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef CG_RENDER_H
|
||||
#define CG_RENDER_H
|
||||
|
||||
struct cg_output;
|
||||
|
||||
void
|
||||
output_render(struct cg_output *output, pixman_region32_t *damage);
|
||||
|
||||
#endif
|
||||
115
seat.h
Normal file
115
seat.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#ifndef CG_SEAT_H
|
||||
#define CG_SEAT_H
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
struct cg_server;
|
||||
struct cg_view;
|
||||
struct wlr_cursor;
|
||||
struct wlr_input_device;
|
||||
struct wlr_seat;
|
||||
struct wlr_xcursor_manager;
|
||||
struct wlr_backend;
|
||||
|
||||
#define DEFAULT_XCURSOR "left_ptr"
|
||||
#define XCURSOR_SIZE 24
|
||||
|
||||
struct cg_keyboard_group {
|
||||
struct cg_keyboard *keyboard;
|
||||
|
||||
struct wlr_keyboard_group *wlr_group;
|
||||
struct cg_seat *seat;
|
||||
struct wl_listener key;
|
||||
struct wl_listener modifiers;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct cg_seat {
|
||||
struct wlr_seat *seat;
|
||||
struct cg_server *server;
|
||||
struct wl_listener destroy;
|
||||
|
||||
struct wl_list keyboards;
|
||||
struct wl_list keyboard_groups;
|
||||
struct wl_list pointers;
|
||||
struct wl_list touch;
|
||||
struct wl_listener new_input;
|
||||
|
||||
struct wlr_cursor *cursor;
|
||||
struct wlr_xcursor_manager *xcursor_manager;
|
||||
struct wl_listener cursor_motion;
|
||||
struct wl_listener cursor_motion_absolute;
|
||||
struct wl_listener cursor_button;
|
||||
struct wl_listener cursor_axis;
|
||||
struct wl_listener cursor_frame;
|
||||
|
||||
int32_t touch_id;
|
||||
double touch_lx;
|
||||
double touch_ly;
|
||||
struct wl_listener touch_down;
|
||||
struct wl_listener touch_up;
|
||||
struct wl_listener touch_motion;
|
||||
|
||||
struct wl_list drag_icons;
|
||||
struct wl_listener request_start_drag;
|
||||
struct wl_listener start_drag;
|
||||
|
||||
struct wl_listener request_set_cursor;
|
||||
struct wl_listener request_set_selection;
|
||||
struct wl_listener request_set_primary_selection;
|
||||
|
||||
uint16_t mode;
|
||||
uint16_t default_mode;
|
||||
|
||||
struct wl_shm *shm; // Shared memory
|
||||
|
||||
struct cg_view *focused_view;
|
||||
};
|
||||
|
||||
struct cg_keyboard {
|
||||
struct wl_list link; // seat::keyboards
|
||||
struct cg_seat *seat;
|
||||
struct wlr_input_device *device;
|
||||
|
||||
struct wl_listener destroy;
|
||||
struct keybinding **repeat_keybinding;
|
||||
struct wl_event_source *key_repeat_timer;
|
||||
};
|
||||
|
||||
struct cg_pointer {
|
||||
struct wl_list link; // seat::pointers
|
||||
struct cg_seat *seat;
|
||||
struct wlr_input_device *device;
|
||||
|
||||
struct wl_listener destroy;
|
||||
};
|
||||
|
||||
struct cg_touch {
|
||||
struct wl_list link; // seat::touch
|
||||
struct cg_seat *seat;
|
||||
struct wlr_input_device *device;
|
||||
|
||||
struct wl_listener destroy;
|
||||
};
|
||||
|
||||
struct cg_drag_icon {
|
||||
struct wl_list link; // seat::drag_icons
|
||||
struct cg_seat *seat;
|
||||
struct wlr_drag_icon *wlr_drag_icon;
|
||||
|
||||
/* The drag icon has a position in layout coordinates. */
|
||||
double lx, ly;
|
||||
|
||||
struct wl_listener destroy;
|
||||
};
|
||||
|
||||
struct cg_seat *
|
||||
seat_create(struct cg_server *server, struct wlr_backend *backend);
|
||||
void
|
||||
seat_destroy(struct cg_seat *seat);
|
||||
struct cg_view *
|
||||
seat_get_focus(const struct cg_seat *seat);
|
||||
void
|
||||
seat_set_focus(struct cg_seat *seat, struct cg_view *view);
|
||||
|
||||
#endif
|
||||
49
server.c
Normal file
49
server.c
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Cagebreak: A Wayland tiling compositor.
|
||||
*
|
||||
* Copyright (C) 2020 The Cagebreak Authors
|
||||
* Copyright (C) 2018-2019 Jente Hidskes
|
||||
*
|
||||
* See the LICENSE file accompanying this file.
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_box.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_surface.h>
|
||||
|
||||
#include "output.h"
|
||||
#include "seat.h"
|
||||
#include "server.h"
|
||||
#include "view.h"
|
||||
#include "workspace.h"
|
||||
#include "xdg_shell.h"
|
||||
#if CG_HAS_XWAYLAND
|
||||
#include "xwayland.h"
|
||||
#endif
|
||||
|
||||
void
|
||||
display_terminate(struct cg_server *server) {
|
||||
if(server == NULL) {
|
||||
return;
|
||||
}
|
||||
server->running = false;
|
||||
wl_display_terminate(server->wl_display);
|
||||
}
|
||||
|
||||
/* Returns the index of a mode given its name or "-1" if the mode is not found.
|
||||
*/
|
||||
int
|
||||
get_mode_index_from_name(char *const *modes, const char *mode_name) {
|
||||
for(int i = 0; modes[i] != NULL; ++i) {
|
||||
if(strcmp(modes[i], mode_name) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
56
server.h
Normal file
56
server.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef CG_SERVER_H
|
||||
#define CG_SERVER_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||
|
||||
struct cg_seat;
|
||||
struct cg_output;
|
||||
struct keybinding_list;
|
||||
struct wlr_output_layout;
|
||||
struct wlr_idle_inhibit_manager_v1;
|
||||
|
||||
struct cg_server {
|
||||
struct wl_display *wl_display;
|
||||
struct wl_event_loop *event_loop;
|
||||
|
||||
struct cg_seat *seat;
|
||||
struct wlr_backend* backend;
|
||||
struct wlr_idle *idle;
|
||||
struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1;
|
||||
struct wl_listener new_idle_inhibitor_v1;
|
||||
struct wl_list inhibitors;
|
||||
|
||||
struct wlr_output_layout *output_layout;
|
||||
struct wl_list outputs;
|
||||
struct cg_output *curr_output;
|
||||
struct wl_listener new_output;
|
||||
|
||||
struct wl_listener xdg_toplevel_decoration;
|
||||
struct wl_listener new_xdg_shell_surface;
|
||||
#if CG_HAS_XWAYLAND
|
||||
struct wl_listener new_xwayland_surface;
|
||||
#endif
|
||||
|
||||
struct keybinding_list *keybindings;
|
||||
|
||||
enum wl_output_transform output_transform;
|
||||
|
||||
bool running;
|
||||
char **modes;
|
||||
uint16_t nws;
|
||||
uint16_t message_timeout;
|
||||
float *bg_color;
|
||||
#ifdef DEBUG
|
||||
bool debug_damage_tracking;
|
||||
#endif
|
||||
};
|
||||
|
||||
void
|
||||
display_terminate(struct cg_server *server);
|
||||
int
|
||||
get_mode_index_from_name(char *const *modes, const char *mode_name);
|
||||
|
||||
#endif
|
||||
35
util.c
Normal file
35
util.c
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Cagebreak: A Wayland tiling compositor.
|
||||
*
|
||||
* Copyright (C) 2020 The Cagebreak Authors
|
||||
* Copyright (C) 2019 The Sway authors
|
||||
*
|
||||
* See the LICENSE file accompanying this file.
|
||||
*/
|
||||
|
||||
#include <wlr/types/wlr_box.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
int
|
||||
scale_length(int length, int offset, double scale) {
|
||||
/**
|
||||
* One does not simply multiply the width by the scale. We allow fractional
|
||||
* scaling, which means the resulting scaled width might be a decimal.
|
||||
* So we round it.
|
||||
*
|
||||
* But even this can produce undesirable results depending on the X or Y
|
||||
* offset of the box. For example, with a scale of 1.5, a box with
|
||||
* width=1 should not scale to 2px if its X coordinate is 1, because the
|
||||
* X coordinate would have scaled to 2px.
|
||||
*/
|
||||
return (int)(round((offset + length) * scale) - round(offset * scale));
|
||||
}
|
||||
|
||||
void
|
||||
scale_box(struct wlr_box *box, double scale) {
|
||||
box->width = scale_length(box->width, box->x, scale);
|
||||
box->height = scale_length(box->height, box->y, scale);
|
||||
box->x = (int)round(box->x * scale);
|
||||
box->y = (int)round(box->y * scale);
|
||||
}
|
||||
13
util.h
Normal file
13
util.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef CG_UTIL_H
|
||||
#define CG_UTIL_H
|
||||
|
||||
struct wlr_box;
|
||||
|
||||
/** Apply scale to a width or height. */
|
||||
int
|
||||
scale_length(int length, int offset, double scale);
|
||||
|
||||
void
|
||||
scale_box(struct wlr_box *box, double scale);
|
||||
|
||||
#endif
|
||||
320
view.c
Normal file
320
view.c
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* Cagebreak: A Wayland tiling compositor.
|
||||
*
|
||||
* Copyright (C) 2020 The Cagebreak Authors
|
||||
* Copyright (C) 2018-2020 Jente Hidskes
|
||||
*
|
||||
* See the LICENSE file accompanying this file.
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_box.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_surface.h>
|
||||
#include <wlr/types/wlr_output_damage.h>
|
||||
|
||||
#include "output.h"
|
||||
#include "seat.h"
|
||||
#include "server.h"
|
||||
#include "workspace.h"
|
||||
#include "xdg_shell.h"
|
||||
#include "view.h"
|
||||
#if CG_HAS_XWAYLAND
|
||||
#include "xwayland.h"
|
||||
#endif
|
||||
|
||||
struct cg_view*
|
||||
view_get_prev_view(struct cg_view *view) {
|
||||
struct cg_view *prev = NULL;
|
||||
|
||||
struct cg_view *it_view;
|
||||
wl_list_for_each(it_view, &view->workspace->views, link) {
|
||||
if(!view_is_visible(it_view) && view != it_view) {
|
||||
prev = it_view;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
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_part(child->view);
|
||||
}
|
||||
|
||||
static void
|
||||
subsurface_create(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->view, wlr_subsurface);
|
||||
}
|
||||
|
||||
void
|
||||
view_child_finish(struct cg_view_child *child) {
|
||||
if(!child) {
|
||||
return;
|
||||
}
|
||||
|
||||
view_damage_whole(child->view);
|
||||
|
||||
wl_list_remove(&child->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 *view,
|
||||
struct wlr_surface *wlr_surface) {
|
||||
child->view = view;
|
||||
child->wlr_surface = wlr_surface;
|
||||
|
||||
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 *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, 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(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
|
||||
view_is_primary(const struct cg_view *view) {
|
||||
return view->impl->is_primary(view);
|
||||
}
|
||||
|
||||
struct cg_tile *
|
||||
view_get_tile(const struct cg_view *view) {
|
||||
|
||||
bool first = true;
|
||||
for(struct cg_tile *tile = view->workspace->focused_tile;
|
||||
first || view->workspace->focused_tile != tile; tile = tile->next) {
|
||||
first = false;
|
||||
if(tile->view == view) {
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
view_is_visible(const struct cg_view *view) {
|
||||
#if CG_HAS_XWAYLAND
|
||||
if(view->type == CG_XWAYLAND_VIEW && !xwayland_view_should_manage(view)) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return view_get_tile(view) != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
view_damage(struct cg_view *view, bool whole) {
|
||||
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
|
||||
view_activate(struct cg_view *view, bool activate) {
|
||||
if(view != NULL) {
|
||||
view->impl->activate(view, activate);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
view_maximize(struct cg_view *view, const struct wlr_box *tile_box) {
|
||||
view->ox = tile_box->x;
|
||||
view->oy = tile_box->y;
|
||||
view->impl->maximize(view, tile_box->width, tile_box->height);
|
||||
}
|
||||
|
||||
void
|
||||
view_position(struct cg_view *view) {
|
||||
struct wlr_box *tile_workspace_box =
|
||||
&view->workspace->output
|
||||
->workspaces[view->workspace->output->curr_workspace]
|
||||
->focused_tile->tile;
|
||||
|
||||
view_maximize(view, tile_workspace_box);
|
||||
}
|
||||
|
||||
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
|
||||
view_unmap(struct cg_view *view) {
|
||||
#if CG_HAS_XWAYLAND
|
||||
if((view->type != CG_XWAYLAND_VIEW || xwayland_view_should_manage(view)))
|
||||
#endif
|
||||
{
|
||||
if(view_is_visible(view)) {
|
||||
struct cg_view* prev = view_get_prev_view(view);
|
||||
struct cg_tile* view_tile = view_get_tile(view);
|
||||
wlr_output_damage_add_box(view_tile->workspace->output->damage, &view_tile->tile);
|
||||
if(seat_get_focus(view->workspace->server->seat) == view) {
|
||||
seat_set_focus(view->workspace->server->seat, prev);
|
||||
} else {
|
||||
view_tile->view = prev;
|
||||
if(prev != NULL) {
|
||||
view_maximize(prev, &view_tile->tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if CG_HAS_XWAYLAND
|
||||
else {
|
||||
view_damage_whole(view);
|
||||
}
|
||||
#endif
|
||||
|
||||
wl_list_remove(&view->link);
|
||||
|
||||
wl_list_remove(&view->new_subsurface.link);
|
||||
|
||||
struct cg_view_child *child, *tmp;
|
||||
wl_list_for_each_safe(child, tmp, &view->children, link) {
|
||||
child->destroy(child);
|
||||
}
|
||||
|
||||
view->wlr_surface = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
view_map(struct cg_view *view, struct wlr_surface *surface) {
|
||||
struct cg_output *output = view->workspace->output;
|
||||
view->wlr_surface = surface;
|
||||
|
||||
struct wlr_subsurface *subsurface;
|
||||
wl_list_for_each(subsurface, &view->wlr_surface->subsurfaces, parent_link) {
|
||||
subsurface_create(view, subsurface);
|
||||
}
|
||||
|
||||
view->new_subsurface.notify = handle_new_subsurface;
|
||||
wl_signal_add(&view->wlr_surface->events.new_subsurface,
|
||||
&view->new_subsurface);
|
||||
|
||||
#if CG_HAS_XWAYLAND
|
||||
/* We shouldn't position override-redirect windows. They set
|
||||
their own (x,y) coordinates in handle_wayland_surface_map. */
|
||||
if(view->type == CG_XWAYLAND_VIEW && !xwayland_view_should_manage(view)) {
|
||||
wl_list_insert(
|
||||
&output->workspaces[output->curr_workspace]->unmanaged_views,
|
||||
&view->link);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
view_position(view);
|
||||
wl_list_insert(&output->workspaces[output->curr_workspace]->views,
|
||||
&view->link);
|
||||
}
|
||||
seat_set_focus(output->server->seat, view);
|
||||
}
|
||||
|
||||
void
|
||||
view_destroy(struct cg_view *view) {
|
||||
struct cg_output* curr_output = view->workspace->server->curr_output;
|
||||
if(view->wlr_surface != NULL) {
|
||||
view_unmap(view);
|
||||
}
|
||||
|
||||
view->impl->destroy(view);
|
||||
view_activate(curr_output->workspaces[curr_output->curr_workspace]->focused_tile->view, true);
|
||||
}
|
||||
|
||||
void
|
||||
view_init(struct cg_view *view, struct cg_workspace *ws, enum cg_view_type type,
|
||||
const struct cg_view_impl *impl) {
|
||||
view->workspace = ws;
|
||||
view->type = type;
|
||||
view->impl = impl;
|
||||
|
||||
wl_list_init(&view->children);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
115
view.h
Normal file
115
view.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#ifndef CG_VIEW_H
|
||||
#define CG_VIEW_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_surface.h>
|
||||
|
||||
struct cg_server;
|
||||
struct wlr_box;
|
||||
struct wlr_surface;
|
||||
|
||||
enum cg_view_type {
|
||||
CG_XDG_SHELL_VIEW,
|
||||
#if CG_HAS_XWAYLAND
|
||||
CG_XWAYLAND_VIEW,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct cg_view {
|
||||
struct cg_workspace *workspace;
|
||||
struct wl_list link; // server::views
|
||||
struct wl_list children; // cg_view_child::link
|
||||
struct wlr_surface *wlr_surface;
|
||||
|
||||
/* The view has a position in output coordinates. */
|
||||
int ox, oy;
|
||||
|
||||
enum cg_view_type type;
|
||||
const struct cg_view_impl *impl;
|
||||
|
||||
struct wl_listener new_subsurface;
|
||||
};
|
||||
|
||||
struct cg_view_impl {
|
||||
char *(*get_title)(const struct cg_view *view);
|
||||
void (*get_geometry)(const struct cg_view *view, int *width_out,
|
||||
int *height_out);
|
||||
bool (*is_primary)(const struct cg_view *view);
|
||||
void (*activate)(struct cg_view *view, bool activate);
|
||||
void (*maximize)(struct cg_view *view, int width, int height);
|
||||
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 wlr_surface *wlr_surface;
|
||||
struct wl_list 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 *
|
||||
view_get_tile(const struct cg_view *view);
|
||||
bool
|
||||
view_is_primary(const struct cg_view *view);
|
||||
bool
|
||||
view_is_visible(const struct cg_view *view);
|
||||
void
|
||||
view_damage_part(struct cg_view *view);
|
||||
void
|
||||
view_damage_whole(struct cg_view *view);
|
||||
void
|
||||
view_activate(struct cg_view *view, bool activate);
|
||||
void
|
||||
view_position(struct cg_view *view);
|
||||
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);
|
||||
void
|
||||
view_maximize(struct cg_view *view, const struct wlr_box *tile_box);
|
||||
void
|
||||
view_map(struct cg_view *view, struct wlr_surface *surface);
|
||||
void
|
||||
view_destroy(struct cg_view *view);
|
||||
void
|
||||
view_init(struct cg_view *view, struct cg_workspace *ws, enum cg_view_type type,
|
||||
const struct cg_view_impl *impl);
|
||||
|
||||
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 *view,
|
||||
struct wlr_surface *wlr_surface);
|
||||
|
||||
#endif
|
||||
70
workspace.c
Normal file
70
workspace.c
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Cagebreak: A Wayland tiling compositor.
|
||||
*
|
||||
* Copyright (C) 2020 The Cagebreak Authors
|
||||
* Copyright (C) 2018-2019 Jente Hidskes
|
||||
*
|
||||
* See the LICENSE file accompanying this file.
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
|
||||
#include "message.h"
|
||||
#include "output.h"
|
||||
#include "server.h"
|
||||
#include "view.h"
|
||||
#include "workspace.h"
|
||||
|
||||
void
|
||||
full_screen_workspace_tiles(const struct cg_output *output,
|
||||
struct cg_workspace *workspace) {
|
||||
workspace->server = output->server;
|
||||
workspace->focused_tile = calloc(1, sizeof(struct cg_tile));
|
||||
workspace->focused_tile->workspace = workspace;
|
||||
workspace->focused_tile->next = workspace->focused_tile;
|
||||
workspace->focused_tile->prev = workspace->focused_tile;
|
||||
workspace->focused_tile->tile.x = 0;
|
||||
workspace->focused_tile->tile.y = 0;
|
||||
struct wlr_box *output_box = wlr_output_layout_get_box(
|
||||
output->server->output_layout, output->wlr_output);
|
||||
workspace->focused_tile->tile.width = output_box->width;
|
||||
workspace->focused_tile->tile.height = output_box->height;
|
||||
workspace->focused_tile->view = NULL;
|
||||
}
|
||||
|
||||
struct cg_workspace *
|
||||
full_screen_workspace(struct cg_output *output) {
|
||||
struct cg_workspace *workspace = calloc(1, sizeof(struct cg_workspace));
|
||||
full_screen_workspace_tiles(output, workspace);
|
||||
workspace->output = output;
|
||||
return workspace;
|
||||
}
|
||||
|
||||
void
|
||||
workspace_focus_tile(struct cg_workspace *ws, struct cg_tile *tile) {
|
||||
ws->focused_tile = tile;
|
||||
struct wlr_box *box = malloc(sizeof(struct wlr_box));
|
||||
box->x = tile->tile.x + tile->tile.width / 2;
|
||||
box->y = tile->tile.y + tile->tile.height / 2;
|
||||
message_printf_pos(ws->output, box, CG_MESSAGE_CENTER, "Current frame");
|
||||
}
|
||||
|
||||
void
|
||||
workspace_free_tiles(struct cg_workspace *workspace) {
|
||||
workspace->focused_tile->prev->next = NULL;
|
||||
while(workspace->focused_tile != NULL) {
|
||||
struct cg_tile *next = workspace->focused_tile->next;
|
||||
free(workspace->focused_tile);
|
||||
workspace->focused_tile = next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
workspace_free(struct cg_workspace *workspace) {
|
||||
workspace_free_tiles(workspace);
|
||||
free(workspace);
|
||||
}
|
||||
38
workspace.h
Normal file
38
workspace.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef CG_WORKSPACE_H
|
||||
#define CG_WORKSPACE_H
|
||||
|
||||
#include <wlr/types/wlr_box.h>
|
||||
|
||||
struct cg_output;
|
||||
struct cg_server;
|
||||
|
||||
struct cg_tile {
|
||||
struct cg_workspace *workspace;
|
||||
struct wlr_box tile;
|
||||
struct cg_view *view;
|
||||
struct cg_tile *next;
|
||||
struct cg_tile *prev;
|
||||
};
|
||||
|
||||
struct cg_workspace {
|
||||
struct cg_server *server;
|
||||
struct wl_list views;
|
||||
struct wl_list unmanaged_views;
|
||||
struct cg_output *output;
|
||||
|
||||
struct cg_tile *focused_tile;
|
||||
};
|
||||
|
||||
struct cg_workspace *
|
||||
full_screen_workspace(struct cg_output *output);
|
||||
void
|
||||
full_screen_workspace_tiles(const struct cg_output *output,
|
||||
struct cg_workspace *workspace);
|
||||
void
|
||||
workspace_free_tiles(struct cg_workspace *workspace);
|
||||
void
|
||||
workspace_free(struct cg_workspace *workspace);
|
||||
void
|
||||
workspace_focus_tile(struct cg_workspace *ws, struct cg_tile *tile);
|
||||
|
||||
#endif
|
||||
355
xdg_shell.c
Normal file
355
xdg_shell.c
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
* Cagebreak: A Wayland tiling compositor.
|
||||
*
|
||||
* Copyright (C) 2020 The Cagebreak Authors
|
||||
* Copyright (C) 2018-2019 Jente Hidskes
|
||||
*
|
||||
* See the LICENSE file accompanying this file.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_box.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
#include "output.h"
|
||||
#include "server.h"
|
||||
#include "view.h"
|
||||
#include "workspace.h"
|
||||
#include "xdg_shell.h"
|
||||
|
||||
static void
|
||||
xdg_decoration_handle_destroy(struct wl_listener *listener, void *_data) {
|
||||
struct cg_xdg_decoration *xdg_decoration =
|
||||
wl_container_of(listener, xdg_decoration, destroy);
|
||||
|
||||
wl_list_remove(&xdg_decoration->destroy.link);
|
||||
wl_list_remove(&xdg_decoration->request_mode.link);
|
||||
free(xdg_decoration);
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_decoration_handle_request_mode(struct wl_listener *listener, void *_data) {
|
||||
struct cg_xdg_decoration *xdg_decoration =
|
||||
wl_container_of(listener, xdg_decoration, request_mode);
|
||||
enum wlr_xdg_toplevel_decoration_v1_mode mode;
|
||||
|
||||
mode = WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
|
||||
wlr_xdg_toplevel_decoration_v1_set_mode(xdg_decoration->wlr_decoration,
|
||||
mode);
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_popup_destroy(struct cg_view_child *child) {
|
||||
if(!child) {
|
||||
return;
|
||||
}
|
||||
|
||||
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->workspace->output->server->output_layout;
|
||||
struct wlr_box *view_output_box = wlr_output_layout_get_box(
|
||||
output_layout, view->workspace->output->wlr_output);
|
||||
struct wlr_output *wlr_output = wlr_output_layout_output_at(
|
||||
output_layout, view_output_box->x + view->ox + popup_box->x,
|
||||
view_output_box->y + view->oy + popup_box->y);
|
||||
struct wlr_box *output_box =
|
||||
wlr_output_layout_get_box(output_layout, wlr_output);
|
||||
|
||||
struct wlr_box output_toplevel_box = {.x = -view->ox,
|
||||
.y = -view->oy,
|
||||
.width = output_box->width,
|
||||
.height = output_box->height};
|
||||
|
||||
wlr_xdg_popup_unconstrain_from_box(popup->wlr_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, 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 *
|
||||
xdg_shell_view_from_view(struct cg_view *view) {
|
||||
return (struct cg_xdg_shell_view *)view;
|
||||
}
|
||||
|
||||
static const struct cg_xdg_shell_view *
|
||||
xdg_shell_view_from_const_view(const struct cg_view *view) {
|
||||
return (const struct cg_xdg_shell_view *)view;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_title(const struct cg_view *view) {
|
||||
const struct cg_xdg_shell_view *xdg_shell_view =
|
||||
xdg_shell_view_from_const_view(view);
|
||||
return xdg_shell_view->xdg_surface->toplevel->title;
|
||||
}
|
||||
|
||||
static void
|
||||
get_geometry(const struct cg_view *view, int *width_out, int *height_out) {
|
||||
const struct cg_xdg_shell_view *xdg_shell_view =
|
||||
xdg_shell_view_from_const_view(view);
|
||||
struct wlr_box geom;
|
||||
|
||||
wlr_xdg_surface_get_geometry(xdg_shell_view->xdg_surface, &geom);
|
||||
*width_out = geom.width;
|
||||
*height_out = geom.height;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_primary(const struct cg_view *view) {
|
||||
const struct cg_xdg_shell_view *xdg_shell_view =
|
||||
xdg_shell_view_from_const_view(view);
|
||||
struct wlr_xdg_surface *parent =
|
||||
xdg_shell_view->xdg_surface->toplevel->parent;
|
||||
/* FIXME: role is 0? */
|
||||
return parent == NULL; /*&& role == WLR_XDG_SURFACE_ROLE_TOPLEVEL */
|
||||
}
|
||||
|
||||
static void
|
||||
activate(struct cg_view *view, bool activate) {
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
maximize(struct cg_view *view, int width, int height) {
|
||||
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_maximized(xdg_shell_view->xdg_surface, true);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(struct cg_view *view) {
|
||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(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(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
|
||||
handle_xdg_shell_surface_request_fullscreen(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct cg_xdg_shell_view *xdg_shell_view =
|
||||
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) {
|
||||
struct cg_xdg_shell_view *xdg_shell_view =
|
||||
wl_container_of(listener, xdg_shell_view, commit);
|
||||
struct cg_view *view = &xdg_shell_view->view;
|
||||
view_damage_part(view);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_xdg_shell_surface_unmap(struct wl_listener *listener, void *_data) {
|
||||
struct cg_xdg_shell_view *xdg_shell_view =
|
||||
wl_container_of(listener, xdg_shell_view, unmap);
|
||||
struct cg_view *view = &xdg_shell_view->view;
|
||||
|
||||
wl_list_remove(&xdg_shell_view->commit.link);
|
||||
|
||||
view_unmap(view);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_xdg_shell_surface_map(struct wl_listener *listener, void *_data) {
|
||||
struct cg_xdg_shell_view *xdg_shell_view =
|
||||
wl_container_of(listener, xdg_shell_view, map);
|
||||
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);
|
||||
|
||||
view_map(view, xdg_shell_view->xdg_surface->surface);
|
||||
view_damage_whole(view);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_xdg_shell_surface_destroy(struct wl_listener *listener, void *_data) {
|
||||
struct cg_xdg_shell_view *xdg_shell_view =
|
||||
wl_container_of(listener, xdg_shell_view, destroy);
|
||||
struct cg_view *view = &xdg_shell_view->view;
|
||||
|
||||
wl_list_remove(&xdg_shell_view->map.link);
|
||||
wl_list_remove(&xdg_shell_view->unmap.link);
|
||||
wl_list_remove(&xdg_shell_view->destroy.link);
|
||||
wl_list_remove(&xdg_shell_view->request_fullscreen.link);
|
||||
wl_list_remove(&xdg_shell_view->new_popup.link);
|
||||
xdg_shell_view->xdg_surface = NULL;
|
||||
|
||||
view_destroy(view);
|
||||
}
|
||||
|
||||
static const struct cg_view_impl xdg_shell_view_impl = {
|
||||
.get_title = get_title,
|
||||
.get_geometry = get_geometry,
|
||||
.is_primary = is_primary,
|
||||
.activate = activate,
|
||||
.maximize = maximize,
|
||||
.destroy = destroy,
|
||||
.for_each_surface = for_each_surface,
|
||||
.for_each_popup = for_each_popup,
|
||||
.wlr_surface_at = wlr_surface_at,
|
||||
};
|
||||
|
||||
void
|
||||
handle_xdg_shell_surface_new(struct wl_listener *listener, void *data) {
|
||||
struct cg_server *server =
|
||||
wl_container_of(listener, server, new_xdg_shell_surface);
|
||||
struct wlr_xdg_surface *xdg_surface = data;
|
||||
|
||||
if(xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
|
||||
return;
|
||||
}
|
||||
|
||||
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,
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace],
|
||||
CG_XDG_SHELL_VIEW, &xdg_shell_view_impl);
|
||||
|
||||
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_shell_view->new_popup.notify = handle_new_xdg_popup;
|
||||
wl_signal_add(&xdg_surface->events.new_popup, &xdg_shell_view->new_popup);
|
||||
}
|
||||
|
||||
void
|
||||
handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data) {
|
||||
struct cg_server *server =
|
||||
wl_container_of(listener, server, xdg_toplevel_decoration);
|
||||
struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration = data;
|
||||
|
||||
struct cg_xdg_decoration *xdg_decoration =
|
||||
calloc(1, sizeof(struct cg_xdg_decoration));
|
||||
if(!xdg_decoration) {
|
||||
return;
|
||||
}
|
||||
|
||||
xdg_decoration->wlr_decoration = wlr_decoration;
|
||||
xdg_decoration->server = server;
|
||||
|
||||
xdg_decoration->destroy.notify = xdg_decoration_handle_destroy;
|
||||
wl_signal_add(&wlr_decoration->events.destroy, &xdg_decoration->destroy);
|
||||
xdg_decoration->request_mode.notify = xdg_decoration_handle_request_mode;
|
||||
wl_signal_add(&wlr_decoration->events.request_mode,
|
||||
&xdg_decoration->request_mode);
|
||||
|
||||
xdg_decoration_handle_request_mode(&xdg_decoration->request_mode,
|
||||
wlr_decoration);
|
||||
}
|
||||
41
xdg_shell.h
Normal file
41
xdg_shell.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef CG_XDG_SHELL_H
|
||||
#define CG_XDG_SHELL_H
|
||||
|
||||
#include "view.h"
|
||||
|
||||
struct cg_xdg_shell_view {
|
||||
struct cg_view view;
|
||||
struct wlr_xdg_surface *xdg_surface;
|
||||
|
||||
struct wl_listener destroy;
|
||||
struct wl_listener unmap;
|
||||
struct wl_listener map;
|
||||
struct wl_listener commit;
|
||||
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 wlr_xdg_toplevel_decoration_v1 *wlr_decoration;
|
||||
struct cg_server *server;
|
||||
struct wl_listener destroy;
|
||||
struct wl_listener request_mode;
|
||||
};
|
||||
|
||||
void
|
||||
handle_xdg_shell_surface_new(struct wl_listener *listener, void *data);
|
||||
|
||||
void
|
||||
handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data);
|
||||
|
||||
#endif
|
||||
226
xwayland.c
Normal file
226
xwayland.c
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* Cagebreak: A Wayland tiling compositor.
|
||||
*
|
||||
* Copyright (C) 2020 The Cagebreak Authors
|
||||
* Copyright (C) 2018-2020 Jente Hidskes
|
||||
*
|
||||
* See the LICENSE file accompanying this file.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_box.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_output_damage.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <wlr/xwayland.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "output.h"
|
||||
#include "server.h"
|
||||
#include "view.h"
|
||||
#include "workspace.h"
|
||||
#include "xwayland.h"
|
||||
|
||||
struct cg_xwayland_view *
|
||||
xwayland_view_from_view(struct cg_view *view) {
|
||||
return (struct cg_xwayland_view *)view;
|
||||
}
|
||||
|
||||
static const struct cg_xwayland_view *
|
||||
xwayland_view_from_const_view(const struct cg_view *view) {
|
||||
return (const struct cg_xwayland_view *)view;
|
||||
}
|
||||
|
||||
bool
|
||||
xwayland_view_should_manage(const struct cg_view *view) {
|
||||
const struct cg_xwayland_view *xwayland_view =
|
||||
xwayland_view_from_const_view(view);
|
||||
struct wlr_xwayland_surface *xwayland_surface =
|
||||
xwayland_view->xwayland_surface;
|
||||
return !xwayland_surface->override_redirect;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_title(const struct cg_view *view) {
|
||||
const struct cg_xwayland_view *xwayland_view =
|
||||
xwayland_view_from_const_view(view);
|
||||
return xwayland_view->xwayland_surface->title;
|
||||
}
|
||||
|
||||
static void
|
||||
get_geometry(const struct cg_view *view, int *width_out, int *height_out) {
|
||||
const struct cg_xwayland_view *xwayland_view =
|
||||
xwayland_view_from_const_view(view);
|
||||
*width_out = xwayland_view->xwayland_surface->width;
|
||||
*height_out = xwayland_view->xwayland_surface->height;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_primary(const struct cg_view *view) {
|
||||
const struct cg_xwayland_view *xwayland_view =
|
||||
xwayland_view_from_const_view(view);
|
||||
struct wlr_xwayland_surface *parent =
|
||||
xwayland_view->xwayland_surface->parent;
|
||||
return parent == NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
activate(struct cg_view *view, bool activate) {
|
||||
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
||||
wlr_xwayland_surface_activate(xwayland_view->xwayland_surface, activate);
|
||||
}
|
||||
|
||||
static void
|
||||
maximize(struct cg_view *view, int width, int height) {
|
||||
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
||||
struct cg_output *output = view->workspace->output;
|
||||
|
||||
struct wlr_box* box = wlr_output_layout_get_box(view->workspace->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(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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
wlr_xwayland_surface_configure(
|
||||
xwayland_view->xwayland_surface, view->ox + box->x,
|
||||
view->oy + box->y, width, height);
|
||||
wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(struct cg_view *view) {
|
||||
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(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
|
||||
handle_xwayland_surface_request_fullscreen(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct cg_xwayland_view *xwayland_view =
|
||||
wl_container_of(listener, xwayland_view, request_fullscreen);
|
||||
struct wlr_xwayland_surface *xwayland_surface =
|
||||
xwayland_view->xwayland_surface;
|
||||
wlr_xwayland_surface_set_fullscreen(xwayland_view->xwayland_surface,
|
||||
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;
|
||||
view_damage_part(view);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_xwayland_surface_unmap(struct wl_listener *listener, void *_data) {
|
||||
struct cg_xwayland_view *xwayland_view =
|
||||
wl_container_of(listener, xwayland_view, unmap);
|
||||
struct cg_view *view = &xwayland_view->view;
|
||||
|
||||
wl_list_remove(&xwayland_view->commit.link);
|
||||
|
||||
view_unmap(view);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_xwayland_surface_map(struct wl_listener *listener, void *_data) {
|
||||
struct cg_xwayland_view *xwayland_view =
|
||||
wl_container_of(listener, xwayland_view, map);
|
||||
struct cg_view *view = &xwayland_view->view;
|
||||
|
||||
if(!xwayland_view_should_manage(view)) {
|
||||
view->ox = xwayland_view->xwayland_surface->x;
|
||||
view->oy = xwayland_view->xwayland_surface->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_damage_whole(view);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_xwayland_surface_destroy(struct wl_listener *listener, void *_data) {
|
||||
struct cg_xwayland_view *xwayland_view =
|
||||
wl_container_of(listener, xwayland_view, destroy);
|
||||
struct cg_view *view = &xwayland_view->view;
|
||||
|
||||
wl_list_remove(&xwayland_view->map.link);
|
||||
wl_list_remove(&xwayland_view->unmap.link);
|
||||
wl_list_remove(&xwayland_view->destroy.link);
|
||||
wl_list_remove(&xwayland_view->request_fullscreen.link);
|
||||
xwayland_view->xwayland_surface = NULL;
|
||||
|
||||
view_destroy(view);
|
||||
}
|
||||
|
||||
static const struct cg_view_impl xwayland_view_impl = {
|
||||
.get_title = get_title,
|
||||
.get_geometry = get_geometry,
|
||||
.is_primary = is_primary,
|
||||
.activate = activate,
|
||||
.maximize = maximize,
|
||||
.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
|
||||
handle_xwayland_surface_new(struct wl_listener *listener, void *data) {
|
||||
struct cg_server *server =
|
||||
wl_container_of(listener, server, new_xwayland_surface);
|
||||
struct wlr_xwayland_surface *xwayland_surface = data;
|
||||
|
||||
struct cg_xwayland_view *xwayland_view =
|
||||
calloc(1, sizeof(struct cg_xwayland_view));
|
||||
if(!xwayland_view) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate XWayland view");
|
||||
return;
|
||||
}
|
||||
|
||||
view_init(
|
||||
&xwayland_view->view,
|
||||
server->curr_output->workspaces[server->curr_output->curr_workspace],
|
||||
CG_XWAYLAND_VIEW, &xwayland_view_impl);
|
||||
xwayland_view->xwayland_surface = xwayland_surface;
|
||||
|
||||
xwayland_view->map.notify = handle_xwayland_surface_map;
|
||||
wl_signal_add(&xwayland_surface->events.map, &xwayland_view->map);
|
||||
xwayland_view->unmap.notify = handle_xwayland_surface_unmap;
|
||||
wl_signal_add(&xwayland_surface->events.unmap, &xwayland_view->unmap);
|
||||
xwayland_view->destroy.notify = handle_xwayland_surface_destroy;
|
||||
wl_signal_add(&xwayland_surface->events.destroy, &xwayland_view->destroy);
|
||||
xwayland_view->request_fullscreen.notify =
|
||||
handle_xwayland_surface_request_fullscreen;
|
||||
wl_signal_add(&xwayland_surface->events.request_fullscreen,
|
||||
&xwayland_view->request_fullscreen);
|
||||
}
|
||||
26
xwayland.h
Normal file
26
xwayland.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef CG_XWAYLAND_H
|
||||
#define CG_XWAYLAND_H
|
||||
|
||||
#include "view.h"
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/xwayland.h>
|
||||
|
||||
struct cg_xwayland_view {
|
||||
struct cg_view view;
|
||||
struct wlr_xwayland_surface *xwayland_surface;
|
||||
|
||||
struct wl_listener destroy;
|
||||
struct wl_listener unmap;
|
||||
struct wl_listener map;
|
||||
struct wl_listener commit;
|
||||
struct wl_listener request_fullscreen;
|
||||
};
|
||||
|
||||
struct cg_xwayland_view *
|
||||
xwayland_view_from_view(struct cg_view *view);
|
||||
bool
|
||||
xwayland_view_should_manage(const struct cg_view *view);
|
||||
void
|
||||
handle_xwayland_surface_new(struct wl_listener *listener, void *data);
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue