From ba6246ada199beafdc9c3585000bf9f59e4e91f8 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Wed, 29 Nov 2017 09:03:12 -0500 Subject: working meson build --- meson.build | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 meson.build (limited to 'meson.build') diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..06eed97f --- /dev/null +++ b/meson.build @@ -0,0 +1,66 @@ +project( + 'sway', + 'c', + license: 'MIT', + default_options: [ + 'c_std=c11', + 'warning_level=2', + 'werror=true', + ], +) + +cc = meson.get_compiler('c') + +jsonc = dependency('json-c', version: '>=0.12.1') +pcre = dependency('libpcre') +wlroots = dependency('wlroots') +wayland_server = dependency('wayland-server') +wayland_client = dependency('wayland-client') +wayland_egl = dependency('wayland-egl') +wayland_protos = dependency('wayland-protocols') +xkbcommon = dependency('xkbcommon') +pango = dependency('pango') +pixman = dependency('pixman-1') +libcap = dependency('libcap') + +math = cc.find_library('m') + +# TODO version +add_project_arguments('-DSWAY_VERSION="5"', language: 'c') + +sway_inc = include_directories('include') + +sway_sources = [ + 'sway/main.c', + 'sway/server.c', + 'sway/commands.c', + 'sway/commands/exit.c', + 'sway/ipc-json.c', + 'sway/ipc-server.c', + 'sway/desktop/output.c', + 'sway/desktop/xdg_shell_v6.c', + 'sway/tree/container.c', + 'sway/tree/layout.c', + 'sway/tree/workspace.c', + 'common/log.c', + 'common/list.c', + 'common/stringop.c', + 'common/readline.c', + 'common/ipc-client.c', +] + +sway_deps = [ + pixman, + wayland_server, + jsonc, + wlroots, + libcap, + math, +] + +executable( + 'sway', + sway_sources, + include_directories: [sway_inc], + dependencies: sway_deps +) -- cgit v1.2.3 From 88cc7e31781a0d42bdd4f9eee646c02ff03857c4 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 30 Nov 2017 03:39:27 -0500 Subject: use meson subdirectories --- common/meson.build | 8 ++++++++ meson.build | 35 ++--------------------------------- sway/meson.build | 29 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 33 deletions(-) create mode 100644 common/meson.build create mode 100644 sway/meson.build (limited to 'meson.build') diff --git a/common/meson.build b/common/meson.build new file mode 100644 index 00000000..ca1f1752 --- /dev/null +++ b/common/meson.build @@ -0,0 +1,8 @@ +sway_common = files( + 'log.c', + 'list.c', + 'util.c', + 'stringop.c', + 'readline.c', + 'ipc-client.c' +) diff --git a/meson.build b/meson.build index 06eed97f..a28e1097 100644 --- a/meson.build +++ b/meson.build @@ -30,37 +30,6 @@ add_project_arguments('-DSWAY_VERSION="5"', language: 'c') sway_inc = include_directories('include') -sway_sources = [ - 'sway/main.c', - 'sway/server.c', - 'sway/commands.c', - 'sway/commands/exit.c', - 'sway/ipc-json.c', - 'sway/ipc-server.c', - 'sway/desktop/output.c', - 'sway/desktop/xdg_shell_v6.c', - 'sway/tree/container.c', - 'sway/tree/layout.c', - 'sway/tree/workspace.c', - 'common/log.c', - 'common/list.c', - 'common/stringop.c', - 'common/readline.c', - 'common/ipc-client.c', -] +subdir('common') +subdir('sway') -sway_deps = [ - pixman, - wayland_server, - jsonc, - wlroots, - libcap, - math, -] - -executable( - 'sway', - sway_sources, - include_directories: [sway_inc], - dependencies: sway_deps -) diff --git a/sway/meson.build b/sway/meson.build new file mode 100644 index 00000000..1f17ed31 --- /dev/null +++ b/sway/meson.build @@ -0,0 +1,29 @@ +sway_sources = sway_common + files( + 'main.c', + 'server.c', + 'commands.c', + 'commands/exit.c', + 'ipc-json.c', + 'ipc-server.c', + 'desktop/output.c', + 'desktop/xdg_shell_v6.c', + 'tree/container.c', + 'tree/layout.c', + 'tree/workspace.c', +) + +sway_deps = [ + pixman, + wayland_server, + jsonc, + wlroots, + libcap, + math, +] + +executable( + 'sway', + sway_sources, + include_directories: [sway_inc], + dependencies: sway_deps +) -- cgit v1.2.3 From 6924064766ac772564d1b924df8c2071c1660235 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 30 Nov 2017 03:41:51 -0500 Subject: fix meson.build whitespace --- meson.build | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'meson.build') diff --git a/meson.build b/meson.build index a28e1097..6f2681af 100644 --- a/meson.build +++ b/meson.build @@ -11,18 +11,17 @@ project( cc = meson.get_compiler('c') -jsonc = dependency('json-c', version: '>=0.12.1') -pcre = dependency('libpcre') -wlroots = dependency('wlroots') +jsonc = dependency('json-c', version: '>=0.12.1') +pcre = dependency('libpcre') +wlroots = dependency('wlroots') wayland_server = dependency('wayland-server') wayland_client = dependency('wayland-client') wayland_egl = dependency('wayland-egl') wayland_protos = dependency('wayland-protocols') xkbcommon = dependency('xkbcommon') -pango = dependency('pango') -pixman = dependency('pixman-1') -libcap = dependency('libcap') - +pango = dependency('pango') +pixman = dependency('pixman-1') +libcap = dependency('libcap') math = cc.find_library('m') # TODO version -- cgit v1.2.3 From 86d1ecf783f8fd164ed63501c859b65cfc6bc022 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 30 Nov 2017 04:29:32 -0500 Subject: meson add version info --- meson.build | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'meson.build') diff --git a/meson.build b/meson.build index 6f2681af..af383fbf 100644 --- a/meson.build +++ b/meson.build @@ -24,8 +24,11 @@ pixman = dependency('pixman-1') libcap = dependency('libcap') math = cc.find_library('m') -# TODO version -add_project_arguments('-DSWAY_VERSION="5"', language: 'c') +git_commit_hash = run_command(['git', 'describe', '--always', '--tags']).stdout().strip() +git_branch = run_command(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() +version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch) + +add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') sway_inc = include_directories('include') -- cgit v1.2.3 From 739bafbd8ecdbbf2eb3fd42e1b3d9249236a3fcf Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 30 Nov 2017 04:48:08 -0500 Subject: make meson version configurable --- meson.build | 16 +++++++++++++--- meson_options.txt | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 meson_options.txt (limited to 'meson.build') diff --git a/meson.build b/meson.build index af383fbf..ac161987 100644 --- a/meson.build +++ b/meson.build @@ -23,10 +23,20 @@ pango = dependency('pango') pixman = dependency('pixman-1') libcap = dependency('libcap') math = cc.find_library('m') +git = find_program('git', required: false) -git_commit_hash = run_command(['git', 'describe', '--always', '--tags']).stdout().strip() -git_branch = run_command(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() -version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch) +version = get_option('sway_version') +if version != '' + version = '"@0@"'.format(version) +else + if not git.found() + error('git is required to make the version string') + endif + + git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip() + git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() + version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch) +endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 00000000..5015a986 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('sway_version', type : 'string', description : 'The version string reported in `sway --version`.') -- cgit v1.2.3 From 2f3633433e2f1d840f3a98ca777d79d658a66e3f Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 30 Nov 2017 08:31:13 -0500 Subject: add meson install files --- config.in | 4 ++-- meson.build | 54 ++++++++++++++++++++++++++++++++++++++++++++++- meson_options.txt | 3 ++- security.d/00-defaults.in | 18 ++++++++-------- sway/meson.build | 3 ++- 5 files changed, 68 insertions(+), 14 deletions(-) (limited to 'meson.build') diff --git a/config.in b/config.in index b39a39aa..086b66dc 100644 --- a/config.in +++ b/config.in @@ -21,7 +21,7 @@ set $menu dmenu_run ### Output configuration # # Default wallpaper (more resolutions are available in __DATADIR__/backgrounds/sway/) -output * bg __DATADIR__/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill +output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill # # Example configuration: # @@ -194,4 +194,4 @@ bar { } } -include __SYSCONFDIR__/sway/config.d/* +include @sysconfdir@/sway/config.d/* diff --git a/meson.build b/meson.build index ac161987..494f2866 100644 --- a/meson.build +++ b/meson.build @@ -11,6 +11,11 @@ project( cc = meson.get_compiler('c') +is_freebsd = host_machine.system().startswith('freebsd') +datadir = get_option('datadir') +sysconfdir = get_option('sysconfdir') +prefix = get_option('prefix') + jsonc = dependency('json-c', version: '>=0.12.1') pcre = dependency('libpcre') wlroots = dependency('wlroots') @@ -37,7 +42,6 @@ else git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch) endif - add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') sway_inc = include_directories('include') @@ -45,3 +49,51 @@ sway_inc = include_directories('include') subdir('common') subdir('sway') +config = configuration_data() +config.set('sysconfdir', join_paths(prefix, sysconfdir)) +config.set('datadir', join_paths(prefix, datadir)) +config.set('prefix', prefix) + +configure_file( + configuration: config, + input: 'config.in', + output: '@BASENAME@', + install_dir: sysconfdir + '/sway' +) + +if is_freebsd + configure_file( + configuration: config, + input: 'security.d/10-freebsd.in', + output: '@BASENAME@', + install_dir: sysconfdir + '/sway/security.d' + ) +else + configure_file( + configuration: config, + input: 'security.d/00-defaults.in', + output: '@BASENAME@', + install_dir: sysconfdir + '/sway/security.d' + ) +endif + +install_data( + 'sway.desktop', + install_dir: datadir + '/wayland-sessions' +) + +if (get_option('default_wallpaper')) + wallpaper_files = files( + 'assets/Sway_Wallpaper_Blue_768x1024.png', + 'assets/Sway_Wallpaper_Blue_768x1024_Portrait.png', + 'assets/Sway_Wallpaper_Blue_1136x640.png', + 'assets/Sway_Wallpaper_Blue_1136x640_Portrait.png', + 'assets/Sway_Wallpaper_Blue_1366x768.png', + 'assets/Sway_Wallpaper_Blue_1920x1080.png', + 'assets/Sway_Wallpaper_Blue_2048x1536.png', + 'assets/Sway_Wallpaper_Blue_2048x1536_Portrait.png', + ) + wallpaper_install_dir = datadir + '/backgrounds/sway' + + install_data(wallpaper_files, install_dir: wallpaper_install_dir) +endif diff --git a/meson_options.txt b/meson_options.txt index 5015a986..03bc1986 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ -option('sway_version', type : 'string', description : 'The version string reported in `sway --version`.') +option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.') +option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') diff --git a/security.d/00-defaults.in b/security.d/00-defaults.in index 05098dea..e4626477 100644 --- a/security.d/00-defaults.in +++ b/security.d/00-defaults.in @@ -6,17 +6,17 @@ # installation. # # DO NOT CHANGE THIS FILE. Override these defaults by writing new files in -# __SYSCONFDIR__/sway/security.d/* +# @sysconfdir@/sway/security.d/* # Configures enabled compositor features for specific programs permit * fullscreen keyboard mouse -permit __PREFIX__/bin/swaylock lock -permit __PREFIX__/bin/swaybg background -permit __PREFIX__/bin/swaygrab screenshot -permit __PREFIX__/bin/swaybar panel +permit @prefix@/bin/swaylock lock +permit @prefix@/bin/swaybg background +permit @prefix@/bin/swaygrab screenshot +permit @prefix@/bin/swaybar panel # Configures enabled IPC features for specific programs -ipc __PREFIX__/bin/swaymsg { +ipc @prefix@/bin/swaymsg { * enabled events { @@ -24,7 +24,7 @@ ipc __PREFIX__/bin/swaymsg { } } -ipc __PREFIX__/bin/swaybar { +ipc @prefix@/bin/swaybar { bar-config enabled outputs enabled workspaces enabled @@ -36,12 +36,12 @@ ipc __PREFIX__/bin/swaybar { } } -ipc __PREFIX__/bin/swaygrab { +ipc @prefix@/bin/swaygrab { outputs enabled tree enabled } -ipc __PREFIX__/bin/swaylock { +ipc @prefix@/bin/swaylock { outputs enabled } diff --git a/sway/meson.build b/sway/meson.build index b02506c8..cf2aa913 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -26,5 +26,6 @@ executable( sway_sources, include_directories: [sway_inc], dependencies: sway_deps, - link_with: [lib_sway_common] + link_with: [lib_sway_common], + install: true ) -- cgit v1.2.3 From 03182706cffc4e996d352c51b143df805f80a928 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 30 Nov 2017 11:34:44 -0500 Subject: meson build manpages --- meson.build | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'meson.build') diff --git a/meson.build b/meson.build index 494f2866..34122acc 100644 --- a/meson.build +++ b/meson.build @@ -29,6 +29,39 @@ pixman = dependency('pixman-1') libcap = dependency('libcap') math = cc.find_library('m') git = find_program('git', required: false) +a2x = find_program('a2x', required: false) + +if a2x.found() + mandir = get_option('mandir') + man_files = [ + 'sway/sway.1.txt', + 'sway/sway.5.txt', + 'sway/sway-bar.5.txt', + 'sway/sway-input.5.txt', + 'sway/sway-security.7.txt', + 'swaymsg/swaymsg.1.txt', + ] + foreach filename : man_files + topic = filename.split('.')[-3].split('/')[-1] + section = filename.split('.')[-2] + + custom_target( + 'man-@0@-@1@'.format(topic, section), + input: filename, + output: '@BASENAME@', + command: [ + a2x, + '--no-xmllint', + '--doctype', 'manpage', + '--format', 'manpage', + '--destination-dir', meson.current_build_dir(), + '@INPUT@' + ], + install: true, + install_dir: '@0@/man@1@'.format(mandir, section) + ) + endforeach +endif version = get_option('sway_version') if version != '' -- cgit v1.2.3 From 1d702b6376b49c2e115c5b985ca241aedc40a48b Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 30 Nov 2017 11:40:49 -0500 Subject: meson build swaymsg --- meson.build | 1 + swaymsg/meson.build | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 swaymsg/meson.build (limited to 'meson.build') diff --git a/meson.build b/meson.build index 34122acc..1c322131 100644 --- a/meson.build +++ b/meson.build @@ -81,6 +81,7 @@ sway_inc = include_directories('include') subdir('common') subdir('sway') +subdir('swaymsg') config = configuration_data() config.set('sysconfdir', join_paths(prefix, sysconfdir)) diff --git a/swaymsg/meson.build b/swaymsg/meson.build new file mode 100644 index 00000000..2d678878 --- /dev/null +++ b/swaymsg/meson.build @@ -0,0 +1,8 @@ +executable( + 'swaymsg', + 'main.c', + include_directories: [sway_inc], + dependencies: [jsonc], + link_with: [lib_sway_common], + install: true +) -- cgit v1.2.3 From fe5c0316096b47d9a9bbede1d35e52e04ad9b40f Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 30 Nov 2017 11:51:58 -0500 Subject: meson zsh completions install --- meson.build | 12 ++++++++++++ meson_options.txt | 1 + 2 files changed, 13 insertions(+) (limited to 'meson.build') diff --git a/meson.build b/meson.build index 1c322131..05d92a91 100644 --- a/meson.build +++ b/meson.build @@ -131,3 +131,15 @@ if (get_option('default_wallpaper')) install_data(wallpaper_files, install_dir: wallpaper_install_dir) endif + +if (get_option('zsh_completions')) + zsh_files = files( + 'completions/zsh/_sway', + 'completions/zsh/_swaygrab', + 'completions/zsh/_swaylock', + 'completions/zsh/_swaymsg', + ) + zsh_install_dir = datadir + '/zsh/site-functions' + + install_data(zsh_files, install_dir: zsh_install_dir) +endif diff --git a/meson_options.txt b/meson_options.txt index 03bc1986..541ccf13 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,2 +1,3 @@ option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.') option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') +option('zsh_completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') -- cgit v1.2.3 From 0f7a7f4084487d846100119b13b26a63ffe9b437 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 1 Dec 2017 05:42:12 -0500 Subject: meson dont warn on unused param --- meson.build | 2 ++ 1 file changed, 2 insertions(+) (limited to 'meson.build') diff --git a/meson.build b/meson.build index 05d92a91..695e237e 100644 --- a/meson.build +++ b/meson.build @@ -9,6 +9,8 @@ project( ], ) +add_project_arguments('-Wno-unused-parameter', language: 'c') + cc = meson.get_compiler('c') is_freebsd = host_machine.system().startswith('freebsd') -- cgit v1.2.3 From 90f7f1a0e61fa20ed1b74b9df057aa70abc791ed Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 5 Dec 2017 10:40:55 +0100 Subject: Add minimal config subsystem --- include/sway/commands.h | 10 + include/sway/config.h | 405 +++++++++++++++++++++++++++++++++++ meson.build | 2 + sway/commands.c | 132 ++++++++++++ sway/commands/exec.c | 6 +- sway/commands/exec_always.c | 6 +- sway/commands/exit.c | 4 +- sway/config.c | 504 ++++++++++++++++++++++++++++++++++++++++++++ sway/main.c | 21 +- sway/meson.build | 2 + sway/security.c | 18 ++ 11 files changed, 1091 insertions(+), 19 deletions(-) create mode 100644 include/sway/config.h create mode 100644 sway/config.c create mode 100644 sway/security.c (limited to 'meson.build') diff --git a/include/sway/commands.h b/include/sway/commands.h index df5c6859..b1f0423d 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -46,6 +46,16 @@ struct cmd_results *checkarg(int argc, const char *name, * Parse and handles a command. */ struct cmd_results *handle_command(char *command); +/** + * Parse and handles a command during config file loading. + * + * Do not use this under normal conditions. + */ +struct cmd_results *config_command(char *command, enum cmd_status block); +/* + * Parses a command policy rule. + */ +struct cmd_results *config_commands_command(char *exec); /** * Allocates a cmd_results object. */ diff --git a/include/sway/config.h b/include/sway/config.h new file mode 100644 index 00000000..56b4e637 --- /dev/null +++ b/include/sway/config.h @@ -0,0 +1,405 @@ +#ifndef _SWAY_CONFIG_H +#define _SWAY_CONFIG_H + +#define PID_WORKSPACE_TIMEOUT 60 + +#include +#include +#include +#include +#include +#include +#include "list.h" +#include "layout.h" +#include "container.h" + +/** + * Describes a variable created via the `set` command. + */ +struct sway_variable { + char *name; + char *value; +}; + +/** + * A key binding and an associated command. + */ +struct sway_binding { + int order; + bool release; + bool bindcode; + list_t *keys; + uint32_t modifiers; + char *command; +}; + +/** + * A mouse binding and an associated command. + */ +struct sway_mouse_binding { + uint32_t button; + char *command; +}; + +/** + * A "mode" of keybindings created via the `mode` command. + */ +struct sway_mode { + char *name; + list_t *bindings; +}; + +/** + * libinput options for input devices + */ +struct input_config { + char *identifier; + + int accel_profile; + int click_method; + int drag_lock; + int dwt; + int left_handed; + int middle_emulation; + int natural_scroll; + float pointer_accel; + int scroll_method; + int send_events; + int tap; + + bool capturable; + struct wlc_geometry region; +}; + +/** + * Size and position configuration for a particular output. + * + * This is set via the `output` command. + */ +struct output_config { + char *name; + int enabled; + int width, height; + int x, y; + int scale; + char *background; + char *background_option; +}; + +/** + * Maps a workspace name to an output name. + * + * Set via `workspace output ` + */ +struct workspace_output { + char *output; + char *workspace; +}; + +struct pid_workspace { + pid_t *pid; + char *workspace; + time_t *time_added; +}; + +struct bar_config { + /** + * One of "dock", "hide", "invisible" + * + * Always visible in dock mode. Visible only when modifier key is held in hide mode. + * Never visible in invisible mode. + */ + char *mode; + /** + * One of "show" or "hide". + * + * In "show" mode, it will always be shown on top of the active workspace. + */ + char *hidden_state; + /** + * Id name used to identify the bar through IPC. + * + * Defaults to bar-x, where x corresponds to the position of the + * embedding bar block in the config file (bar-0, bar-1, ...). + */ + char *id; + uint32_t modifier; + list_t *outputs; + //enum desktop_shell_panel_position position; // TODO + list_t *bindings; + char *status_command; + bool pango_markup; + char *swaybar_command; + char *font; + int height; // -1 not defined + +#ifdef ENABLE_TRAY + // Tray + char *tray_output; + char *icon_theme; + uint32_t tray_padding; + uint32_t activate_button; + uint32_t context_button; + uint32_t secondary_button; +#endif + + bool workspace_buttons; + bool wrap_scroll; + char *separator_symbol; + bool strip_workspace_numbers; + bool binding_mode_indicator; + bool verbose; + pid_t pid; + struct { + char *background; + char *statusline; + char *separator; + char *focused_background; + char *focused_statusline; + char *focused_separator; + char *focused_workspace_border; + char *focused_workspace_bg; + char *focused_workspace_text; + char *active_workspace_border; + char *active_workspace_bg; + char *active_workspace_text; + char *inactive_workspace_border; + char *inactive_workspace_bg; + char *inactive_workspace_text; + char *urgent_workspace_border; + char *urgent_workspace_bg; + char *urgent_workspace_text; + char *binding_mode_border; + char *binding_mode_bg; + char *binding_mode_text; + } colors; +}; + +struct border_colors { + uint32_t border; + uint32_t background; + uint32_t text; + uint32_t indicator; + uint32_t child_border; +}; + +enum edge_border_types { + E_NONE, /**< Don't hide edge borders */ + E_VERTICAL, /**< hide vertical edge borders */ + E_HORIZONTAL, /**< hide horizontal edge borders */ + E_BOTH, /**< hide vertical and horizontal edge borders */ + E_SMART /**< hide both if precisely one window is present in workspace */ +}; + +enum command_context { + CONTEXT_CONFIG = 1, + CONTEXT_BINDING = 2, + CONTEXT_IPC = 4, + CONTEXT_CRITERIA = 8, + CONTEXT_ALL = 0xFFFFFFFF, +}; + +struct command_policy { + char *command; + uint32_t context; +}; + +enum secure_feature { + FEATURE_LOCK = 1, + FEATURE_PANEL = 2, + FEATURE_BACKGROUND = 4, + FEATURE_SCREENSHOT = 8, + FEATURE_FULLSCREEN = 16, + FEATURE_KEYBOARD = 32, + FEATURE_MOUSE = 64, +}; + +struct feature_policy { + char *program; + uint32_t features; +}; + +enum ipc_feature { + IPC_FEATURE_COMMAND = 1, + IPC_FEATURE_GET_WORKSPACES = 2, + IPC_FEATURE_GET_OUTPUTS = 4, + IPC_FEATURE_GET_TREE = 8, + IPC_FEATURE_GET_MARKS = 16, + IPC_FEATURE_GET_BAR_CONFIG = 32, + IPC_FEATURE_GET_VERSION = 64, + IPC_FEATURE_GET_INPUTS = 128, + IPC_FEATURE_EVENT_WORKSPACE = 256, + IPC_FEATURE_EVENT_OUTPUT = 512, + IPC_FEATURE_EVENT_MODE = 1024, + IPC_FEATURE_EVENT_WINDOW = 2048, + IPC_FEATURE_EVENT_BINDING = 4096, + IPC_FEATURE_EVENT_INPUT = 8192, + IPC_FEATURE_GET_CLIPBOARD = 16384, + + IPC_FEATURE_ALL_COMMANDS = 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 16384, + IPC_FEATURE_ALL_EVENTS = 256 | 512 | 1024 | 2048 | 4096 | 8192, + + IPC_FEATURE_ALL = IPC_FEATURE_ALL_COMMANDS | IPC_FEATURE_ALL_EVENTS, +}; + +struct ipc_policy { + char *program; + uint32_t features; +}; + +/** + * The configuration struct. The result of loading a config file. + */ +struct sway_config { + list_t *symbols; + list_t *modes; + list_t *bars; + list_t *cmd_queue; + list_t *workspace_outputs; + list_t *pid_workspaces; + list_t *output_configs; + list_t *input_configs; + list_t *criteria; + list_t *no_focus; + list_t *active_bar_modifiers; + struct sway_mode *current_mode; + struct bar_config *current_bar; + uint32_t floating_mod; + uint32_t dragging_key; + uint32_t resizing_key; + char *floating_scroll_up_cmd; + char *floating_scroll_down_cmd; + char *floating_scroll_left_cmd; + char *floating_scroll_right_cmd; + enum swayc_layouts default_orientation; + enum swayc_layouts default_layout; + char *font; + int font_height; + + // Flags + bool focus_follows_mouse; + bool mouse_warping; + bool force_focus_wrapping; + bool active; + bool failed; + bool reloading; + bool reading; + bool auto_back_and_forth; + bool seamless_mouse; + bool show_marks; + + bool edge_gaps; + bool smart_gaps; + int gaps_inner; + int gaps_outer; + + list_t *config_chain; + const char *current_config; + + enum swayc_border_types border; + enum swayc_border_types floating_border; + int border_thickness; + int floating_border_thickness; + enum edge_border_types hide_edge_borders; + + // border colors + struct { + struct border_colors focused; + struct border_colors focused_inactive; + struct border_colors unfocused; + struct border_colors urgent; + struct border_colors placeholder; + uint32_t background; + } border_colors; + + // floating view + int32_t floating_maximum_width; + int32_t floating_maximum_height; + int32_t floating_minimum_width; + int32_t floating_minimum_height; + + // Security + list_t *command_policies; + list_t *feature_policies; + list_t *ipc_policies; +}; + +void pid_workspace_add(struct pid_workspace *pw); +void free_pid_workspace(struct pid_workspace *pw); + +/** + * Loads the main config from the given path. is_active should be true when + * reloading the config. + */ +bool load_main_config(const char *path, bool is_active); + +/** + * Loads an included config. Can only be used after load_main_config. + */ +bool load_include_configs(const char *path, struct sway_config *config); + +/** + * Reads the config from the given FILE. + */ +bool read_config(FILE *file, struct sway_config *config); + +/** + * Free config struct + */ +void free_config(struct sway_config *config); +/** + * Does variable replacement for a string based on the config's currently loaded variables. + */ +char *do_var_replacement(char *str); + +struct cmd_results *check_security_config(); + +int input_identifier_cmp(const void *item, const void *data); +void merge_input_config(struct input_config *dst, struct input_config *src); +void apply_input_config(struct input_config *ic, struct libinput_device *dev); +void free_input_config(struct input_config *ic); + +int output_name_cmp(const void *item, const void *data); +void merge_output_config(struct output_config *dst, struct output_config *src); +/** Sets up a WLC output handle based on a given output_config. + */ +void apply_output_config(struct output_config *oc, swayc_t *output); +void free_output_config(struct output_config *oc); + +/** + * Updates the list of active bar modifiers + */ +void update_active_bar_modifiers(void); + +int workspace_output_cmp_workspace(const void *a, const void *b); + +int sway_binding_cmp(const void *a, const void *b); +int sway_binding_cmp_qsort(const void *a, const void *b); +int sway_binding_cmp_keys(const void *a, const void *b); +void free_sway_binding(struct sway_binding *sb); +struct sway_binding *sway_binding_dup(struct sway_binding *sb); + +int sway_mouse_binding_cmp(const void *a, const void *b); +int sway_mouse_binding_cmp_qsort(const void *a, const void *b); +int sway_mouse_binding_cmp_buttons(const void *a, const void *b); +void free_sway_mouse_binding(struct sway_mouse_binding *smb); + +void load_swaybars(); +void terminate_swaybg(pid_t pid); + +/** + * Allocate and initialize default bar configuration. + */ +struct bar_config *default_bar_config(void); + +/** + * Global config singleton. + */ +extern struct sway_config *config; + +/** + * Config file currently being read. + */ +extern const char *current_config_path; + +#endif diff --git a/meson.build b/meson.build index 695e237e..8e7b98ed 100644 --- a/meson.build +++ b/meson.build @@ -65,6 +65,8 @@ if a2x.found() endforeach endif +add_project_arguments('-DSYSCONFDIR="/@0@"'.format(sysconfdir), language : 'c') + version = get_option('sway_version') if version != '' version = '"@0@"'.format(version) diff --git a/sway/commands.c b/sway/commands.c index 5fdcdbb6..17638129 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -7,6 +7,8 @@ #include #include #include "sway/commands.h" +#include "sway/config.h" +#include "sway/security.h" #include "stringop.h" #include "log.h" @@ -200,6 +202,136 @@ cleanup: return results; } +// this is like handle_command above, except: +// 1) it ignores empty commands (empty lines) +// 2) it does variable substitution +// 3) it doesn't split commands (because the multiple commands are supposed to +// be chained together) +// 4) handle_command handles all state internally while config_command has some +// state handled outside (notably the block mode, in read_config) +struct cmd_results *config_command(char *exec, enum cmd_status block) { + struct cmd_results *results = NULL; + int argc; + char **argv = split_args(exec, &argc); + if (!argc) { + results = cmd_results_new(CMD_SUCCESS, NULL, NULL); + goto cleanup; + } + + sway_log(L_INFO, "handling config command '%s'", exec); + // Endblock + if (**argv == '}') { + results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); + goto cleanup; + } + struct cmd_handler *handler = find_handler(argv[0], block); + if (!handler) { + char *input = argv[0] ? argv[0] : "(empty)"; + results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); + goto cleanup; + } + int i; + // Var replacement, for all but first argument of set + // TODO commands + for (i = /*handler->handle == cmd_set ? 2 :*/ 1; i < argc; ++i) { + argv[i] = do_var_replacement(argv[i]); + unescape_string(argv[i]); + } + /* Strip quotes for first argument. + * TODO This part needs to be handled much better */ + if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { + strip_quotes(argv[1]); + } + if (handler->handle) { + results = handler->handle(argc-1, argv+1); + } else { + results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented"); + } + +cleanup: + free_argv(argc, argv); + return results; +} + +struct cmd_results *config_commands_command(char *exec) { + struct cmd_results *results = NULL; + int argc; + char **argv = split_args(exec, &argc); + if (!argc) { + results = cmd_results_new(CMD_SUCCESS, NULL, NULL); + goto cleanup; + } + + // Find handler for the command this is setting a policy for + char *cmd = argv[0]; + + if (strcmp(cmd, "}") == 0) { + results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); + goto cleanup; + } + + struct cmd_handler *handler = find_handler(cmd, CMD_BLOCK_END); + if (!handler && strcmp(cmd, "*") != 0) { + char *input = cmd ? cmd : "(empty)"; + results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); + goto cleanup; + } + + enum command_context context = 0; + + struct { + char *name; + enum command_context context; + } context_names[] = { + { "config", CONTEXT_CONFIG }, + { "binding", CONTEXT_BINDING }, + { "ipc", CONTEXT_IPC }, + { "criteria", CONTEXT_CRITERIA }, + { "all", CONTEXT_ALL }, + }; + + for (int i = 1; i < argc; ++i) { + size_t j; + for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) { + if (strcmp(context_names[j].name, argv[i]) == 0) { + break; + } + } + if (j == sizeof(context_names) / sizeof(context_names[0])) { + results = cmd_results_new(CMD_INVALID, cmd, + "Invalid command context %s", argv[i]); + goto cleanup; + } + context |= context_names[j].context; + } + + struct command_policy *policy = NULL; + for (int i = 0; i < config->command_policies->length; ++i) { + struct command_policy *p = config->command_policies->items[i]; + if (strcmp(p->command, cmd) == 0) { + policy = p; + break; + } + } + if (!policy) { + policy = alloc_command_policy(cmd); + sway_assert(policy, "Unable to allocate security policy"); + if (policy) { + list_add(config->command_policies, policy); + } + } + policy->context = context; + + sway_log(L_INFO, "Set command policy for %s to %d", + policy->command, policy->context); + + results = cmd_results_new(CMD_SUCCESS, NULL, NULL); + +cleanup: + free_argv(argc, argv); + return results; +} + struct cmd_results *cmd_results_new(enum cmd_status status, const char *input, const char *format, ...) { struct cmd_results *results = malloc(sizeof(struct cmd_results)); diff --git a/sway/commands/exec.c b/sway/commands/exec.c index dd71500a..fbbc4941 100644 --- a/sway/commands/exec.c +++ b/sway/commands/exec.c @@ -1,16 +1,16 @@ #include #include "sway/commands.h" +#include "sway/config.h" #include "log.h" #include "stringop.h" struct cmd_results *cmd_exec(int argc, char **argv) { - // TODO: config - /*if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL); + if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL); if (config->reloading) { char *args = join_args(argv, argc); sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); free(args); return cmd_results_new(CMD_SUCCESS, NULL, NULL); - }*/ + } return cmd_exec_always(argc, argv); } diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 0a252e7b..9527a487 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -5,15 +5,15 @@ #include #include #include "sway/commands.h" -#include "sway/workspace.h" +#include "sway/config.h" #include "sway/container.h" +#include "sway/workspace.h" #include "log.h" #include "stringop.h" struct cmd_results *cmd_exec_always(int argc, char **argv) { struct cmd_results *error = NULL; - // TODO: config - //if (!config->active) return cmd_results_new(CMD_DEFER, NULL, NULL); + if (!config->active) return cmd_results_new(CMD_DEFER, NULL, NULL); if ((error = checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0))) { return error; } diff --git a/sway/commands/exit.c b/sway/commands/exit.c index d294e344..4bb6a97b 100644 --- a/sway/commands/exit.c +++ b/sway/commands/exit.c @@ -1,19 +1,17 @@ #include #include "sway/commands.h" +#include "sway/config.h" void sway_terminate(int exit_code); struct cmd_results *cmd_exit(int argc, char **argv) { struct cmd_results *error = NULL; - /* TODO if (config->reading) { return cmd_results_new(CMD_FAILURE, "exit", "Can't be used in config file."); } - */ if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) { return error; } sway_terminate(0); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } - diff --git a/sway/config.c b/sway/config.c new file mode 100644 index 00000000..475e8b04 --- /dev/null +++ b/sway/config.c @@ -0,0 +1,504 @@ +#define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/layout.h" +#include "readline.h" +#include "stringop.h" +#include "list.h" +#include "log.h" + +struct sway_config *config = NULL; + +void free_config(struct sway_config *config) { + // TODO +} + +static void config_defaults(struct sway_config *config) { + if (!(config->symbols = create_list())) goto cleanup; + if (!(config->modes = create_list())) goto cleanup; + if (!(config->bars = create_list())) goto cleanup; + if (!(config->workspace_outputs = create_list())) goto cleanup; + if (!(config->pid_workspaces = create_list())) goto cleanup; + if (!(config->criteria = create_list())) goto cleanup; + if (!(config->no_focus = create_list())) goto cleanup; + if (!(config->input_configs = create_list())) goto cleanup; + if (!(config->output_configs = create_list())) goto cleanup; + + if (!(config->cmd_queue = create_list())) goto cleanup; + + if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) goto cleanup; + if (!(config->current_mode->name = malloc(sizeof("default")))) goto cleanup; + strcpy(config->current_mode->name, "default"); + if (!(config->current_mode->bindings = create_list())) goto cleanup; + list_add(config->modes, config->current_mode); + + config->floating_mod = 0; + config->dragging_key = BTN_LEFT; + config->resizing_key = BTN_RIGHT; + if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup; + if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup; + if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup; + if (!(config->floating_scroll_right_cmd = strdup(""))) goto cleanup; + config->default_layout = L_NONE; + config->default_orientation = L_NONE; + if (!(config->font = strdup("monospace 10"))) goto cleanup; + // TODO: border + //config->font_height = get_font_text_height(config->font); + + // floating view + config->floating_maximum_width = 0; + config->floating_maximum_height = 0; + config->floating_minimum_width = 75; + config->floating_minimum_height = 50; + + // Flags + config->focus_follows_mouse = true; + config->mouse_warping = true; + config->reloading = false; + config->active = false; + config->failed = false; + config->auto_back_and_forth = false; + config->seamless_mouse = true; + config->reading = false; + config->show_marks = true; + + config->edge_gaps = true; + config->smart_gaps = false; + config->gaps_inner = 0; + config->gaps_outer = 0; + + if (!(config->active_bar_modifiers = create_list())) goto cleanup; + + if (!(config->config_chain = create_list())) goto cleanup; + config->current_config = NULL; + + // borders + config->border = B_NORMAL; + config->floating_border = B_NORMAL; + config->border_thickness = 2; + config->floating_border_thickness = 2; + config->hide_edge_borders = E_NONE; + + // border colors + config->border_colors.focused.border = 0x4C7899FF; + config->border_colors.focused.background = 0x285577FF; + config->border_colors.focused.text = 0xFFFFFFFF; + config->border_colors.focused.indicator = 0x2E9EF4FF; + config->border_colors.focused.child_border = 0x285577FF; + + config->border_colors.focused_inactive.border = 0x333333FF; + config->border_colors.focused_inactive.background = 0x5F676AFF; + config->border_colors.focused_inactive.text = 0xFFFFFFFF; + config->border_colors.focused_inactive.indicator = 0x484E50FF; + config->border_colors.focused_inactive.child_border = 0x5F676AFF; + + config->border_colors.unfocused.border = 0x333333FF; + config->border_colors.unfocused.background = 0x222222FF; + config->border_colors.unfocused.text = 0x888888FF; + config->border_colors.unfocused.indicator = 0x292D2EFF; + config->border_colors.unfocused.child_border = 0x222222FF; + + config->border_colors.urgent.border = 0x2F343AFF; + config->border_colors.urgent.background = 0x900000FF; + config->border_colors.urgent.text = 0xFFFFFFFF; + config->border_colors.urgent.indicator = 0x900000FF; + config->border_colors.urgent.child_border = 0x900000FF; + + config->border_colors.placeholder.border = 0x000000FF; + config->border_colors.placeholder.background = 0x0C0C0CFF; + config->border_colors.placeholder.text = 0xFFFFFFFF; + config->border_colors.placeholder.indicator = 0x000000FF; + config->border_colors.placeholder.child_border = 0x0C0C0CFF; + + config->border_colors.background = 0xFFFFFFFF; + + // Security + if (!(config->command_policies = create_list())) goto cleanup; + if (!(config->feature_policies = create_list())) goto cleanup; + if (!(config->ipc_policies = create_list())) goto cleanup; + + return; +cleanup: + sway_abort("Unable to allocate config structures"); +} + +static bool file_exists(const char *path) { + return path && access(path, R_OK) != -1; +} + +static char *get_config_path(void) { + static const char *config_paths[] = { + "$HOME/.sway/config", + "$XDG_CONFIG_HOME/sway/config", + "$HOME/.i3/config", + "$XDG_CONFIG_HOME/i3/config", + SYSCONFDIR "/sway/config", + SYSCONFDIR "/i3/config", + }; + + if (!getenv("XDG_CONFIG_HOME")) { + char *home = getenv("HOME"); + char *config_home = malloc(strlen(home) + strlen("/.config") + 1); + if (!config_home) { + sway_log(L_ERROR, "Unable to allocate $HOME/.config"); + } else { + strcpy(config_home, home); + strcat(config_home, "/.config"); + setenv("XDG_CONFIG_HOME", config_home, 1); + sway_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); + free(config_home); + } + } + + wordexp_t p; + char *path; + + int i; + for (i = 0; i < (int)(sizeof(config_paths) / sizeof(char *)); ++i) { + if (wordexp(config_paths[i], &p, 0) == 0) { + path = strdup(p.we_wordv[0]); + wordfree(&p); + if (file_exists(path)) { + return path; + } + } + } + + return NULL; // Not reached +} + +const char *current_config_path; + +static bool load_config(const char *path, struct sway_config *config) { + sway_log(L_INFO, "Loading config from %s", path); + current_config_path = path; + + struct stat sb; + if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { + return false; + } + + if (path == NULL) { + sway_log(L_ERROR, "Unable to find a config file!"); + return false; + } + + FILE *f = fopen(path, "r"); + if (!f) { + sway_log(L_ERROR, "Unable to open %s for reading", path); + return false; + } + + bool config_load_success = read_config(f, config); + fclose(f); + + if (!config_load_success) { + sway_log(L_ERROR, "Error(s) loading config!"); + } + + current_config_path = NULL; + return true; +} + +static int qstrcmp(const void* a, const void* b) { + return strcmp(*((char**) a), *((char**) b)); +} + +bool load_main_config(const char *file, bool is_active) { + char *path; + if (file != NULL) { + path = strdup(file); + } else { + path = get_config_path(); + } + + struct sway_config *old_config = config; + config = calloc(1, sizeof(struct sway_config)); + if (!config) { + sway_abort("Unable to allocate config"); + } + + config_defaults(config); + if (is_active) { + sway_log(L_DEBUG, "Performing configuration file reload"); + config->reloading = true; + config->active = true; + } + + config->current_config = path; + list_add(config->config_chain, path); + + config->reading = true; + + // Read security configs + bool success = true; + DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); + if (!dir) { + sway_log(L_ERROR, "%s does not exist, sway will have no security configuration" + " and will probably be broken", SYSCONFDIR "/sway/security.d"); + } else { + list_t *secconfigs = create_list(); + char *base = SYSCONFDIR "/sway/security.d/"; + struct dirent *ent = readdir(dir); + struct stat s; + while (ent != NULL) { + char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1); + strcpy(_path, base); + strcat(_path, ent->d_name); + lstat(_path, &s); + if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') { + list_add(secconfigs, _path); + } + else { + free(_path); + } + ent = readdir(dir); + } + closedir(dir); + + list_qsort(secconfigs, qstrcmp); + for (int i = 0; i < secconfigs->length; ++i) { + char *_path = secconfigs->items[i]; + if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || (((s.st_mode & 0777) != 0644) && (s.st_mode & 0777) != 0444)) { + sway_log(L_ERROR, "Refusing to load %s - it must be owned by root and mode 644 or 444", _path); + success = false; + } else { + success = success && load_config(_path, config); + } + } + + free_flat_list(secconfigs); + } + + success = success && load_config(path, config); + + if (is_active) { + config->reloading = false; + } + + if (old_config) { + free_config(old_config); + } + config->reading = false; + + if (success) { + // TODO: bar + //update_active_bar_modifiers(); + } + + return success; +} + +bool read_config(FILE *file, struct sway_config *config) { + bool success = true; + enum cmd_status block = CMD_BLOCK_END; + + int line_number = 0; + char *line; + while (!feof(file)) { + line = read_line(file); + if (!line) { + continue; + } + line_number++; + line = strip_whitespace(line); + if (line[0] == '#') { + free(line); + continue; + } + struct cmd_results *res; + if (block == CMD_BLOCK_COMMANDS) { + // Special case + res = config_commands_command(line); + } else { + res = config_command(line, block); + } + switch(res->status) { + case CMD_FAILURE: + case CMD_INVALID: + sway_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, line, + res->error, config->current_config); + success = false; + break; + + case CMD_DEFER: + sway_log(L_DEBUG, "Defferring command `%s'", line); + list_add(config->cmd_queue, strdup(line)); + break; + + case CMD_BLOCK_MODE: + if (block == CMD_BLOCK_END) { + block = CMD_BLOCK_MODE; + } else { + sway_log(L_ERROR, "Invalid block '%s'", line); + } + break; + + case CMD_BLOCK_INPUT: + if (block == CMD_BLOCK_END) { + block = CMD_BLOCK_INPUT; + } else { + sway_log(L_ERROR, "Invalid block '%s'", line); + } + break; + + case CMD_BLOCK_BAR: + if (block == CMD_BLOCK_END) { + block = CMD_BLOCK_BAR; + } else { + sway_log(L_ERROR, "Invalid block '%s'", line); + } + break; + + case CMD_BLOCK_BAR_COLORS: + if (block == CMD_BLOCK_BAR) { + block = CMD_BLOCK_BAR_COLORS; + } else { + sway_log(L_ERROR, "Invalid block '%s'", line); + } + break; + + case CMD_BLOCK_COMMANDS: + if (block == CMD_BLOCK_END) { + block = CMD_BLOCK_COMMANDS; + } else { + sway_log(L_ERROR, "Invalid block '%s'", line); + } + break; + + case CMD_BLOCK_IPC: + if (block == CMD_BLOCK_END) { + block = CMD_BLOCK_IPC; + } else { + sway_log(L_ERROR, "Invalid block '%s'", line); + } + break; + + case CMD_BLOCK_IPC_EVENTS: + if (block == CMD_BLOCK_IPC) { + block = CMD_BLOCK_IPC_EVENTS; + } else { + sway_log(L_ERROR, "Invalid block '%s'", line); + } + break; + + case CMD_BLOCK_END: + switch(block) { + case CMD_BLOCK_MODE: + sway_log(L_DEBUG, "End of mode block"); + config->current_mode = config->modes->items[0]; + block = CMD_BLOCK_END; + break; + + case CMD_BLOCK_INPUT: + sway_log(L_DEBUG, "End of input block"); + // TODO: input + //current_input_config = NULL; + block = CMD_BLOCK_END; + break; + + case CMD_BLOCK_BAR: + sway_log(L_DEBUG, "End of bar block"); + config->current_bar = NULL; + block = CMD_BLOCK_END; + break; + + case CMD_BLOCK_BAR_COLORS: + sway_log(L_DEBUG, "End of bar colors block"); + block = CMD_BLOCK_BAR; + break; + + case CMD_BLOCK_COMMANDS: + sway_log(L_DEBUG, "End of commands block"); + block = CMD_BLOCK_END; + break; + + case CMD_BLOCK_IPC: + sway_log(L_DEBUG, "End of IPC block"); + block = CMD_BLOCK_END; + break; + + case CMD_BLOCK_IPC_EVENTS: + sway_log(L_DEBUG, "End of IPC events block"); + block = CMD_BLOCK_IPC; + break; + + case CMD_BLOCK_END: + sway_log(L_ERROR, "Unmatched }"); + break; + + default:; + } + default:; + } + free(line); + free_cmd_results(res); + } + + return success; +} + +char *do_var_replacement(char *str) { + int i; + char *find = str; + while ((find = strchr(find, '$'))) { + // Skip if escaped. + if (find > str && find[-1] == '\\') { + if (find == str + 1 || !(find > str + 1 && find[-2] == '\\')) { + ++find; + continue; + } + } + // Find matching variable + for (i = 0; i < config->symbols->length; ++i) { + struct sway_variable *var = config->symbols->items[i]; + int vnlen = strlen(var->name); + if (strncmp(find, var->name, vnlen) == 0) { + int vvlen = strlen(var->value); + char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); + if (!newstr) { + sway_log(L_ERROR, + "Unable to allocate replacement during variable expansion"); + break; + } + char *newptr = newstr; + int offset = find - str; + strncpy(newptr, str, offset); + newptr += offset; + strncpy(newptr, var->value, vvlen); + newptr += vvlen; + strcpy(newptr, find + vnlen); + free(str); + str = newstr; + find = str + offset + vvlen; + break; + } + } + if (i == config->symbols->length) { + ++find; + } + } + return str; +} diff --git a/sway/main.c b/sway/main.c index bc107309..bc843591 100644 --- a/sway/main.c +++ b/sway/main.c @@ -15,6 +15,7 @@ #include #include #endif +#include "sway/config.h" #include "sway/server.h" #include "sway/layout.h" #include "sway/ipc-server.h" @@ -388,14 +389,14 @@ int main(int argc, char **argv) { ipc_init(&server); log_env(); - //if (validate) { - // bool valid = load_main_config(config_path, false); - // return valid ? 0 : 1; - //} + if (validate) { + bool valid = load_main_config(config_path, false); + return valid ? 0 : 1; + } - //if (!load_main_config(config_path, false)) { - // sway_terminate(EXIT_FAILURE); - //} + if (!load_main_config(config_path, false)) { + sway_terminate(EXIT_FAILURE); + } if (config_path) { free(config_path); @@ -411,9 +412,9 @@ int main(int argc, char **argv) { ipc_terminate(); - //if (config) { - // free_config(config); - //} + if (config) { + free_config(config); + } return exit_value; } diff --git a/sway/meson.build b/sway/meson.build index b224f15f..84f48137 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -5,12 +5,14 @@ sway_sources = files( 'commands/exit.c', 'commands/exec.c', 'commands/exec_always.c', + 'config.c', 'ipc-json.c', 'ipc-server.c', 'desktop/output.c', 'desktop/wl_shell.c', 'desktop/xdg_shell_v6.c', 'desktop/xwayland.c', + 'security.c', 'tree/container.c', 'tree/layout.c', 'tree/workspace.c', diff --git a/sway/security.c b/sway/security.c new file mode 100644 index 00000000..cc0d3f66 --- /dev/null +++ b/sway/security.c @@ -0,0 +1,18 @@ +#define _XOPEN_SOURCE 700 +#include +#include +#include "sway/security.h" + +struct command_policy *alloc_command_policy(const char *command) { + struct command_policy *policy = malloc(sizeof(struct command_policy)); + if (!policy) { + return NULL; + } + policy->command = strdup(command); + if (!policy->command) { + free(policy); + return NULL; + } + policy->context = 0; + return policy; +} -- cgit v1.2.3 From 338a0399f8d7c0ebe9cbb989945d8fd646d8d407 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Wed, 6 Dec 2017 08:28:46 -0500 Subject: input skeleton --- include/sway/input.h | 18 ++++++++++++++++++ meson.build | 1 + sway/meson.build | 2 ++ sway/server.c | 4 ++++ 4 files changed, 25 insertions(+) create mode 100644 include/sway/input.h (limited to 'meson.build') diff --git a/include/sway/input.h b/include/sway/input.h new file mode 100644 index 00000000..21ed61c5 --- /dev/null +++ b/include/sway/input.h @@ -0,0 +1,18 @@ +#ifndef _SWAY_INPUT_H +#define _SWAY_INPUT_H +#include +#include "sway/server.h" +#include "config.h" +#include "list.h" + +struct sway_input { + list_t *input_devices; +}; + +struct input_config *new_input_config(const char* identifier); + +char* libinput_dev_unique_id(struct libinput_device *dev); + +struct sway_input *sway_input_create(struct sway_server *server); + +#endif diff --git a/meson.build b/meson.build index 8e7b98ed..029aea46 100644 --- a/meson.build +++ b/meson.build @@ -29,6 +29,7 @@ xkbcommon = dependency('xkbcommon') pango = dependency('pango') pixman = dependency('pixman-1') libcap = dependency('libcap') +libinput = dependency('libinput') math = cc.find_library('m') git = find_program('git', required: false) a2x = find_program('a2x', required: false) diff --git a/sway/meson.build b/sway/meson.build index 84f48137..8631b9c3 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -2,6 +2,7 @@ sway_sources = files( 'main.c', 'server.c', 'commands.c', + 'input/input.c', 'commands/exit.c', 'commands/exec.c', 'commands/exec_always.c', @@ -25,6 +26,7 @@ sway_deps = [ wlroots, libcap, math, + libinput, ] executable( diff --git a/sway/server.c b/sway/server.c index 024d8429..3873e625 100644 --- a/sway/server.c +++ b/sway/server.c @@ -11,6 +11,7 @@ // TODO WLR: make Xwayland optional #include #include "sway/server.h" +#include "sway/input.h" #include "log.h" bool server_init(struct sway_server *server) { @@ -58,6 +59,9 @@ bool server_init(struct sway_server *server) { wlr_backend_destroy(server->backend); return false; } + + server->input = sway_input_create(server); + return true; } -- cgit v1.2.3 From dc0e6d46fb63aad09b8767d94976c6e56db5ffdb Mon Sep 17 00:00:00 2001 From: Rachel K Date: Thu, 4 Jan 2018 15:43:40 +0000 Subject: update json-c dep to 0.13 --- meson.build | 2 +- swaymsg/main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'meson.build') diff --git a/meson.build b/meson.build index 029aea46..49392126 100644 --- a/meson.build +++ b/meson.build @@ -18,7 +18,7 @@ datadir = get_option('datadir') sysconfdir = get_option('sysconfdir') prefix = get_option('prefix') -jsonc = dependency('json-c', version: '>=0.12.1') +jsonc = dependency('json-c', version: '>=0.13') pcre = dependency('libpcre') wlroots = dependency('wlroots') wayland_server = dependency('wayland-server') diff --git a/swaymsg/main.c b/swaymsg/main.c index 8a720fca..a34eced4 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -156,7 +156,7 @@ static void pretty_print_version(json_object *v) { static void pretty_print_clipboard(json_object *v) { if (success(v, true)) { if (json_object_is_type(v, json_type_array)) { - for (int i = 0; i < json_object_array_length(v); ++i) { + for (size_t i = 0; i < json_object_array_length(v); ++i) { json_object *o = json_object_array_get_idx(v, i); printf("%s\n", json_object_get_string(o)); } -- cgit v1.2.3 From 5e9fe97e36b5e33b9919c2cfee776fc8eac05959 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 18 Feb 2018 11:20:18 -0500 Subject: Add -Wno-unused-function This is temporary, it helps while we work on porting and large swaths of the code are commented out. --- meson.build | 1 + 1 file changed, 1 insertion(+) (limited to 'meson.build') diff --git a/meson.build b/meson.build index 49392126..fc995c81 100644 --- a/meson.build +++ b/meson.build @@ -10,6 +10,7 @@ project( ) add_project_arguments('-Wno-unused-parameter', language: 'c') +add_project_arguments('-Wno-unused-function', language: 'c') cc = meson.get_compiler('c') -- cgit v1.2.3 From bb173672ae1677967ebbe39facb432d06dccd884 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 12 Mar 2018 12:21:46 +0100 Subject: meson: allow wlroots to be a subproject --- .gitignore | 1 + meson.build | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'meson.build') diff --git a/.gitignore b/.gitignore index b6e56f7a..f48e22e5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build/ .lvimrc config-debug wayland-*-protocol.* +/subprojects/wlroots diff --git a/meson.build b/meson.build index fc995c81..f27ac451 100644 --- a/meson.build +++ b/meson.build @@ -21,7 +21,7 @@ prefix = get_option('prefix') jsonc = dependency('json-c', version: '>=0.13') pcre = dependency('libpcre') -wlroots = dependency('wlroots') +wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots']) wayland_server = dependency('wayland-server') wayland_client = dependency('wayland-client') wayland_egl = dependency('wayland-egl') -- cgit v1.2.3 From eccf0b25987b2b194031edf3415e9f09a7ad7119 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 27 Mar 2018 13:35:34 -0400 Subject: Add client protocols and swaybg skeleton --- meson.build | 2 + protocols/meson.build | 49 ++++++ protocols/wlr-layer-shell-unstable-v1.xml | 281 ++++++++++++++++++++++++++++++ swaybg/main.c | 189 +------------------- swaybg/meson.build | 8 + 5 files changed, 347 insertions(+), 182 deletions(-) create mode 100644 protocols/meson.build create mode 100644 protocols/wlr-layer-shell-unstable-v1.xml create mode 100644 swaybg/meson.build (limited to 'meson.build') diff --git a/meson.build b/meson.build index f27ac451..622769a3 100644 --- a/meson.build +++ b/meson.build @@ -85,9 +85,11 @@ add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') sway_inc = include_directories('include') +subdir('protocols') subdir('common') subdir('sway') subdir('swaymsg') +subdir('swaybg') config = configuration_data() config.set('sysconfdir', join_paths(prefix, sysconfdir)) diff --git a/protocols/meson.build b/protocols/meson.build new file mode 100644 index 00000000..73a6fda5 --- /dev/null +++ b/protocols/meson.build @@ -0,0 +1,49 @@ +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@'], +) + +wayland_scanner_code = generator( + wayland_scanner, + output: '@BASENAME@-protocol.c', + arguments: ['code', '@INPUT@', '@OUTPUT@'], +) + +wayland_scanner_client = generator( + wayland_scanner, + output: '@BASENAME@-client-protocol.h', + arguments: ['client-header', '@INPUT@', '@OUTPUT@'], +) + +protocols = [] + +client_protocols = [ + 'wlr-layer-shell-unstable-v1.xml', +] + +wl_protos_src = [] +wl_protos_headers = [] + +foreach p : protocols + xml = join_paths(p) + wl_protos_src += wayland_scanner_code.process(xml) + wl_protos_headers += wayland_scanner_server.process(xml) +endforeach + +foreach p : client_protocols + xml = join_paths(p) + wl_protos_headers += wayland_scanner_client.process(xml) +endforeach + +lib_wl_protos = static_library('wl_protos', wl_protos_src + wl_protos_headers, + dependencies: [wayland_client]) # for the include directory + +sway_protos = declare_dependency( + link_with: lib_wl_protos, + sources: wl_protos_headers, +) diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 00000000..3181c0bb --- /dev/null +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,281 @@ + + + + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + + + + + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + Clients can specify a namespace that defines the purpose of the layer + surface. + + + + + + + + + + + + + + + + + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + + + + + + + + + + + + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (size, anchor, exclusive zone, margin, interactivity) + is double-buffered, and will be applied at the time wl_surface.commit of + the corresponding wl_surface is called. + + + + + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + + + + + + + + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthoginal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + + + + + + + Requests that the compositor avoids occluding an area of the surface + with other surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to an + edge, rather than a corner. The zone is the number of surface-local + coordinates from the edge that are considered exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive excluzive zone. If set to -1, the surface + indicates that it would not like to be moved to accomodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + + + + + + + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + + + + + + + + + + Set to 1 to request that the seat send keyboard events to this layer + surface. For layers below the shell surface layer, the seat will use + normal focus semantics. For layers above the shell surface layers, the + seat will always give exclusive keyboard focus to the top-most layer + which has keyboard interactivity set to true. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Events is double-buffered, see wl_surface.commit. + + + + + + + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + + + + + + + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + + + + + + + This request destroys the layer surface. + + + + + + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + + + + + + + + + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + + + + + + + + + + + + + + + + + diff --git a/swaybg/main.c b/swaybg/main.c index 2fdd4220..94e98228 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -1,20 +1,11 @@ -#include "wayland-desktop-shell-client-protocol.h" +#include #include #include #include -#include -#include -#include #include -#include "client/window.h" -#include "client/registry.h" -#include "client/cairo.h" -#include "log.h" -#include "list.h" -#include "util.h" - -list_t *surfaces; -struct registry *registry; +#include +#include +#include enum scaling_mode { SCALING_MODE_STRETCH, @@ -24,21 +15,11 @@ enum scaling_mode { SCALING_MODE_TILE, }; -void sway_terminate(int exit_code) { - int i; - for (i = 0; i < surfaces->length; ++i) { - struct window *window = surfaces->items[i]; - window_teardown(window); - } - list_free(surfaces); - registry_teardown(registry); - exit(exit_code); -} - bool is_valid_color(const char *color) { int len = strlen(color); if (len != 7 || color[0] != '#') { - sway_log(L_ERROR, "%s is not a valid color for swaybg. Color should be specified as #rrggbb (no alpha).", color); + wlr_log(L_ERROR, "%s is not a valid color for swaybg. " + "Color should be specified as #rrggbb (no alpha).", color); return false; } @@ -53,162 +34,6 @@ bool is_valid_color(const char *color) { } int main(int argc, const char **argv) { - init_log(L_INFO); - surfaces = create_list(); - registry = registry_poll(); - - if (argc != 4) { - sway_abort("Do not run this program manually. See man 5 sway and look for output options."); - } - - if (!registry->desktop_shell) { - sway_abort("swaybg requires the compositor to support the desktop-shell extension."); - } - - int desired_output = atoi(argv[1]); - sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length); - int i; - struct output_state *output = registry->outputs->items[desired_output]; - struct window *window = window_setup(registry, - output->width, output->height, output->scale, false); - if (!window) { - sway_abort("Failed to create surfaces."); - } - desktop_shell_set_background(registry->desktop_shell, output->output, window->surface); - window_make_shell(window); - list_add(surfaces, window); - - if (strcmp(argv[3], "solid_color") == 0 && is_valid_color(argv[2])) { - cairo_set_source_u32(window->cairo, parse_color(argv[2])); - cairo_paint(window->cairo); - window_render(window); - } else { -#ifdef WITH_GDK_PIXBUF - GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(argv[2], &err); - if (!pixbuf) { - sway_abort("Failed to load background image."); - } - cairo_surface_t *image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); - g_object_unref(pixbuf); -#else - cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]); -#endif //WITH_GDK_PIXBUF - if (!image) { - sway_abort("Failed to read background image."); - } - if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { - sway_abort("Failed to read background image: %s." -#ifndef WITH_GDK_PIXBUF - "\nSway was compiled without gdk_pixbuf support, so only" - "\nPNG images can be loaded. This is the likely cause." -#endif //WITH_GDK_PIXBUF - , cairo_status_to_string(cairo_surface_status(image))); - } - double width = cairo_image_surface_get_width(image); - double height = cairo_image_surface_get_height(image); - - const char *scaling_mode_str = argv[3]; - enum scaling_mode scaling_mode = SCALING_MODE_STRETCH; - if (strcmp(scaling_mode_str, "stretch") == 0) { - scaling_mode = SCALING_MODE_STRETCH; - } else if (strcmp(scaling_mode_str, "fill") == 0) { - scaling_mode = SCALING_MODE_FILL; - } else if (strcmp(scaling_mode_str, "fit") == 0) { - scaling_mode = SCALING_MODE_FIT; - } else if (strcmp(scaling_mode_str, "center") == 0) { - scaling_mode = SCALING_MODE_CENTER; - } else if (strcmp(scaling_mode_str, "tile") == 0) { - scaling_mode = SCALING_MODE_TILE; - } else { - sway_abort("Unsupported scaling mode: %s", scaling_mode_str); - } - - int wwidth = window->width * window->scale; - int wheight = window->height * window->scale; - - for (i = 0; i < surfaces->length; ++i) { - struct window *window = surfaces->items[i]; - if (window_prerender(window) && window->cairo) { - switch (scaling_mode) { - case SCALING_MODE_STRETCH: - cairo_scale(window->cairo, - (double) wwidth / width, - (double) wheight / height); - cairo_set_source_surface(window->cairo, image, 0, 0); - break; - case SCALING_MODE_FILL: - { - double window_ratio = (double) wwidth / wheight; - double bg_ratio = width / height; - - if (window_ratio > bg_ratio) { - double scale = (double) wwidth / width; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - 0, - (double) wheight/2 / scale - height/2); - } else { - double scale = (double) wheight / height; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 / scale - width/2, - 0); - } - break; - } - case SCALING_MODE_FIT: - { - double window_ratio = (double) wwidth / wheight; - double bg_ratio = width / height; - - if (window_ratio > bg_ratio) { - double scale = (double) wheight / height; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 / scale - width/2, - 0); - } else { - double scale = (double) wwidth / width; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - 0, - (double) wheight/2 / scale - height/2); - } - break; - } - case SCALING_MODE_CENTER: - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 - width/2, - (double) wheight/2 - height/2); - break; - case SCALING_MODE_TILE: - { - cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); - cairo_set_source(window->cairo, pattern); - break; - } - default: - sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str); - } - - cairo_paint(window->cairo); - - window_render(window); - } - } - - cairo_surface_destroy(image); - } - - while (wl_display_dispatch(registry->display) != -1); - - for (i = 0; i < surfaces->length; ++i) { - struct window *window = surfaces->items[i]; - window_teardown(window); - } - list_free(surfaces); - registry_teardown(registry); + wlr_log_init(L_DEBUG, NULL); return 0; } diff --git a/swaybg/meson.build b/swaybg/meson.build new file mode 100644 index 00000000..47315023 --- /dev/null +++ b/swaybg/meson.build @@ -0,0 +1,8 @@ +executable( + 'swaybg', + 'main.c', + include_directories: [sway_inc], + dependencies: [wayland_client, sway_protos, jsonc, wlroots], + link_with: [lib_sway_common], + install: true +) -- cgit v1.2.3 From 632bb948b7ffbb08a6e965dabf88347afd0a1fa8 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 27 Mar 2018 15:25:25 -0400 Subject: Add solid-color rendering to swaybg --- client/buffer-pool.c | 123 +++++++++++++++++++++++++++++++++ client/meson.build | 21 ++++++ common/cairo.c | 127 ++++++++++++++++++++++++++++++++++ common/meson.build | 21 ++++-- include/buffer_pool.h | 21 ++++++ include/cairo.h | 18 +++++ meson.build | 8 +++ protocols/meson.build | 18 +---- swaybg/main.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++ swaybg/meson.build | 18 ++++- wayland/buffers.c | 27 +++----- 11 files changed, 550 insertions(+), 39 deletions(-) create mode 100644 client/buffer-pool.c create mode 100644 client/meson.build create mode 100644 common/cairo.c create mode 100644 include/buffer_pool.h create mode 100644 include/cairo.h (limited to 'meson.build') diff --git a/client/buffer-pool.c b/client/buffer-pool.c new file mode 100644 index 00000000..26d0f7e5 --- /dev/null +++ b/client/buffer-pool.c @@ -0,0 +1,123 @@ +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "buffer_pool.h" + +static int create_pool_file(size_t size, char **name) { + static const char template[] = "sway-client-XXXXXX"; + const char *path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + return -1; + } + + int ts = (path[strlen(path) - 1] == '/'); + + *name = malloc( + strlen(template) + + strlen(path) + + (ts ? 0 : 1) + 1); + sprintf(*name, "%s%s%s", path, ts ? "" : "/", template); + + int fd = mkstemp(*name); + + if (fd < 0) { + return -1; + } + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} + +static void buffer_release(void *data, struct wl_buffer *wl_buffer) { + struct pool_buffer *buffer = data; + buffer->busy = false; +} + +static const struct wl_buffer_listener buffer_listener = { + .release = buffer_release +}; + +static struct pool_buffer *create_buffer(struct wl_shm *shm, + struct pool_buffer *buf, int32_t width, int32_t height, + uint32_t format) { + uint32_t stride = width * 4; + uint32_t size = stride * height; + + char *name; + int fd = create_pool_file(size, &name); + assert(fd); + void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); + buf->buffer = wl_shm_pool_create_buffer(pool, 0, + width, height, stride, format); + wl_shm_pool_destroy(pool); + close(fd); + unlink(name); + free(name); + fd = -1; + + buf->width = width; + buf->height = height; + buf->surface = cairo_image_surface_create_for_data(data, + CAIRO_FORMAT_ARGB32, width, height, stride); + buf->cairo = cairo_create(buf->surface); + buf->pango = pango_cairo_create_context(buf->cairo); + + wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); + return buf; +} + +static void destroy_buffer(struct pool_buffer *buffer) { + if (buffer->buffer) { + wl_buffer_destroy(buffer->buffer); + } + if (buffer->cairo) { + cairo_destroy(buffer->cairo); + } + if (buffer->surface) { + cairo_surface_destroy(buffer->surface); + } + if (buffer->pango) { + g_object_unref(buffer->pango); + } + memset(buffer, 0, sizeof(struct pool_buffer)); +} + +struct pool_buffer *get_next_buffer(struct wl_shm *shm, + struct pool_buffer pool[static 2], uint32_t width, uint32_t height) { + struct pool_buffer *buffer = NULL; + + for (size_t i = 0; i < 2; ++i) { + if (pool[i].busy) { + continue; + } + buffer = &pool[i]; + } + + if (!buffer) { + return NULL; + } + + if (buffer->width != width || buffer->height != height) { + destroy_buffer(buffer); + } + + if (!buffer->buffer) { + if (!create_buffer(shm, buffer, width, height, + WL_SHM_FORMAT_ARGB8888)) { + return NULL; + } + } + return buffer; +} diff --git a/client/meson.build b/client/meson.build new file mode 100644 index 00000000..597899ce --- /dev/null +++ b/client/meson.build @@ -0,0 +1,21 @@ +deps = [ + cairo, + pango, + pangocairo, + wlroots, + wayland_client, +] + +if gdk_pixbuf.found() + deps += [gdk_pixbuf] +endif + +lib_sway_client = static_library( + 'sway-client', + files( + 'buffer-pool.c', + ), + dependencies: deps, + link_with: [lib_sway_common], + include_directories: sway_inc +) diff --git a/common/cairo.c b/common/cairo.c new file mode 100644 index 00000000..c6bd0da9 --- /dev/null +++ b/common/cairo.c @@ -0,0 +1,127 @@ +#include +#include +#include "cairo.h" +#ifdef WITH_GDK_PIXBUF +#include +#endif + +void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { + cairo_set_source_rgba(cairo, + (color >> (3*8) & 0xFF) / 255.0, + (color >> (2*8) & 0xFF) / 255.0, + (color >> (1*8) & 0xFF) / 255.0, + (color >> (0*8) & 0xFF) / 255.0); +} + +cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, + int width, int height) { + int image_width = cairo_image_surface_get_width(image); + int image_height = cairo_image_surface_get_height(image); + + cairo_surface_t *new = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_t *cairo = cairo_create(new); + cairo_scale(cairo, (double)width / image_width, + (double)height / image_height); + cairo_set_source_surface(cairo, image, 0, 0); + + cairo_paint(cairo); + cairo_destroy(cairo); + return new; +} + +#ifdef WITH_GDK_PIXBUF +cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { + int chan = gdk_pixbuf_get_n_channels(gdkbuf); + if (chan < 3) { + return NULL; + } + + const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf); + if (!gdkpix) { + return NULL; + } + gint w = gdk_pixbuf_get_width(gdkbuf); + gint h = gdk_pixbuf_get_height(gdkbuf); + int stride = gdk_pixbuf_get_rowstride(gdkbuf); + + cairo_format_t fmt = (chan == 3) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32; + cairo_surface_t * cs = cairo_image_surface_create (fmt, w, h); + cairo_surface_flush (cs); + if ( !cs || cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) { + return NULL; + } + + int cstride = cairo_image_surface_get_stride(cs); + unsigned char * cpix = cairo_image_surface_get_data(cs); + + if (chan == 3) { + int i; + for (i = h; i; --i) { + const guint8 *gp = gdkpix; + unsigned char *cp = cpix; + const guint8* end = gp + 3*w; + while (gp < end) { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + cp[0] = gp[2]; + cp[1] = gp[1]; + cp[2] = gp[0]; +#else + cp[1] = gp[0]; + cp[2] = gp[1]; + cp[3] = gp[2]; +#endif + gp += 3; + cp += 4; + } + gdkpix += stride; + cpix += cstride; + } + } else { + /* premul-color = alpha/255 * color/255 * 255 = (alpha*color)/255 + * (z/255) = z/256 * 256/255 = z/256 (1 + 1/255) + * = z/256 + (z/256)/255 = (z + z/255)/256 + * # recurse once + * = (z + (z + z/255)/256)/256 + * = (z + z/256 + z/256/255) / 256 + * # only use 16bit uint operations, loose some precision, + * # result is floored. + * -> (z + z>>8)>>8 + * # add 0x80/255 = 0.5 to convert floor to round + * => (z+0x80 + (z+0x80)>>8 ) >> 8 + * ------ + * tested as equal to lround(z/255.0) for uint z in [0..0xfe02] + */ +#define PREMUL_ALPHA(x,a,b,z) \ + G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \ + G_STMT_END + int i; + for (i = h; i; --i) { + const guint8 *gp = gdkpix; + unsigned char *cp = cpix; + const guint8* end = gp + 4*w; + guint z1, z2, z3; + while (gp < end) { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + PREMUL_ALPHA(cp[0], gp[2], gp[3], z1); + PREMUL_ALPHA(cp[1], gp[1], gp[3], z2); + PREMUL_ALPHA(cp[2], gp[0], gp[3], z3); + cp[3] = gp[3]; +#else + PREMUL_ALPHA(cp[1], gp[0], gp[3], z1); + PREMUL_ALPHA(cp[2], gp[1], gp[3], z2); + PREMUL_ALPHA(cp[3], gp[2], gp[3], z3); + cp[0] = gp[3]; +#endif + gp += 4; + cp += 4; + } + gdkpix += stride; + cpix += cstride; + } +#undef PREMUL_ALPHA + } + cairo_surface_mark_dirty(cs); + return cs; +} +#endif //WITH_GDK_PIXBUF diff --git a/common/meson.build b/common/meson.build index abe0cdcf..01736ca6 100644 --- a/common/meson.build +++ b/common/meson.build @@ -1,12 +1,23 @@ -lib_sway_common = static_library('sway-common', +deps = [ + cairo, + wlroots +] + +if gdk_pixbuf.found() + deps += [gdk_pixbuf] +endif + +lib_sway_common = static_library( + 'sway-common', files( + 'cairo.c', + 'ipc-client.c', 'log.c', 'list.c', - 'util.c', - 'stringop.c', 'readline.c', - 'ipc-client.c' + 'stringop.c', + 'util.c' ), - dependencies: [ wlroots ], + dependencies: deps, include_directories: sway_inc ) diff --git a/include/buffer_pool.h b/include/buffer_pool.h new file mode 100644 index 00000000..cdebd64d --- /dev/null +++ b/include/buffer_pool.h @@ -0,0 +1,21 @@ +#ifndef _SWAY_BUFFERS_H +#define _SWAY_BUFFERS_H +#include +#include +#include +#include +#include + +struct pool_buffer { + struct wl_buffer *buffer; + cairo_surface_t *surface; + cairo_t *cairo; + PangoContext *pango; + uint32_t width, height; + bool busy; +}; + +struct pool_buffer *get_next_buffer(struct wl_shm *shm, + struct pool_buffer pool[static 2], uint32_t width, uint32_t height); + +#endif diff --git a/include/cairo.h b/include/cairo.h new file mode 100644 index 00000000..f5f474d7 --- /dev/null +++ b/include/cairo.h @@ -0,0 +1,18 @@ +#ifndef _SWAY_CAIRO_H +#define _SWAY_CAIRO_H +#include +#include + +void cairo_set_source_u32(cairo_t *cairo, uint32_t color); + +cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, + int width, int height); + +#ifdef WITH_GDK_PIXBUF +#include + +cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( + const GdkPixbuf *gdkbuf); +#endif //WITH_GDK_PIXBUF + +#endif diff --git a/meson.build b/meson.build index 622769a3..0c35b0e5 100644 --- a/meson.build +++ b/meson.build @@ -27,7 +27,10 @@ wayland_client = dependency('wayland-client') wayland_egl = dependency('wayland-egl') wayland_protos = dependency('wayland-protocols') xkbcommon = dependency('xkbcommon') +cairo = dependency('cairo') pango = dependency('pango') +pangocairo = dependency('pangocairo') +gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) pixman = dependency('pixman-1') libcap = dependency('libcap') libinput = dependency('libinput') @@ -35,6 +38,10 @@ math = cc.find_library('m') git = find_program('git', required: false) a2x = find_program('a2x', required: false) +if gdk_pixbuf.found() + add_project_arguments('-DWITH_GDK_PIXBUF', language : 'c') +endif + if a2x.found() mandir = get_option('mandir') man_files = [ @@ -89,6 +96,7 @@ subdir('protocols') subdir('common') subdir('sway') subdir('swaymsg') +subdir('client') subdir('swaybg') config = configuration_data() diff --git a/protocols/meson.build b/protocols/meson.build index 73a6fda5..1fda600e 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -2,12 +2,6 @@ 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@'], -) - wayland_scanner_code = generator( wayland_scanner, output: '@BASENAME@-protocol.c', @@ -20,10 +14,9 @@ wayland_scanner_client = generator( arguments: ['client-header', '@INPUT@', '@OUTPUT@'], ) -protocols = [] - -client_protocols = [ - 'wlr-layer-shell-unstable-v1.xml', +protocols = [ + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], + ['wlr-layer-shell-unstable-v1.xml'] ] wl_protos_src = [] @@ -32,11 +25,6 @@ wl_protos_headers = [] foreach p : protocols xml = join_paths(p) wl_protos_src += wayland_scanner_code.process(xml) - wl_protos_headers += wayland_scanner_server.process(xml) -endforeach - -foreach p : client_protocols - xml = join_paths(p) wl_protos_headers += wayland_scanner_client.process(xml) endforeach diff --git a/swaybg/main.c b/swaybg/main.c index 94e98228..4473869b 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -6,6 +6,10 @@ #include #include #include +#include "buffer_pool.h" +#include "cairo.h" +#include "util.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" enum scaling_mode { SCALING_MODE_STRETCH, @@ -13,6 +17,31 @@ enum scaling_mode { SCALING_MODE_FIT, SCALING_MODE_CENTER, SCALING_MODE_TILE, + SCALING_MODE_SOLID_COLOR, +}; + +struct swaybg_args { + int output_idx; + const char *path; + enum scaling_mode mode; +}; + +struct swaybg_state { + const struct swaybg_args *args; + + struct wl_display *display; + struct wl_compositor *compositor; + struct zwlr_layer_shell_v1 *layer_shell; + struct wl_shm *shm; + + struct wl_output *output; + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; + + bool run_display; + uint32_t width, height; + struct pool_buffer buffers[2]; + struct pool_buffer *current_buffer; }; bool is_valid_color(const char *color) { @@ -33,7 +62,165 @@ bool is_valid_color(const char *color) { return true; } +static void render_frame(struct swaybg_state *state) { + if (!state->run_display) { + return; + } + + state->current_buffer = get_next_buffer(state->shm, + state->buffers, state->width, state->height); + cairo_t *cairo = state->current_buffer->cairo; + + switch (state->args->mode) { + case SCALING_MODE_SOLID_COLOR: + cairo_set_source_u32(cairo, parse_color(state->args->path)); + cairo_paint(cairo); + break; + default: + exit(1); + break; + } + + wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0); + wl_surface_damage(state->surface, 0, 0, state->width, state->height); + wl_surface_commit(state->surface); +} + +static void layer_surface_configure(void *data, + struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t width, uint32_t height) { + struct swaybg_state *state = data; + state->width = width; + state->height = height; + render_frame(state); + zwlr_layer_surface_v1_ack_configure(surface, serial); +} + +static void layer_surface_closed(void *data, + struct zwlr_layer_surface_v1 *surface) { + struct swaybg_state *state = data; + zwlr_layer_surface_v1_destroy(state->layer_surface); + wl_surface_destroy(state->surface); + state->run_display = false; +} + +struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_configure, + .closed = layer_surface_closed, +}; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + struct swaybg_state *state = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + state->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 1); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + state->shm = wl_registry_bind(registry, name, + &wl_shm_interface, 1); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + static int output_idx = 0; + if (output_idx == state->args->output_idx) { + state->output = wl_registry_bind(registry, name, + &wl_output_interface, 1); + } + output_idx++; + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + state->layer_shell = wl_registry_bind( + registry, name, &zwlr_layer_shell_v1_interface, 1); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) { + // who cares +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; + int main(int argc, const char **argv) { + struct swaybg_args args = {0}; + struct swaybg_state state = {0}; + state.args = &args; wlr_log_init(L_DEBUG, NULL); + + if (argc != 4) { + wlr_log(L_ERROR, "Do not run this program manually. " + "See man 5 sway and look for output options."); + return 1; + } + args.output_idx = atoi(argv[1]); + args.path = argv[2]; + args.mode = atoi(argv[3]); + + args.mode = SCALING_MODE_STRETCH; + if (strcmp(argv[3], "stretch") == 0) { + args.mode = SCALING_MODE_STRETCH; + } else if (strcmp(argv[3], "fill") == 0) { + args.mode = SCALING_MODE_FILL; + } else if (strcmp(argv[3], "fit") == 0) { + args.mode = SCALING_MODE_FIT; + } else if (strcmp(argv[3], "center") == 0) { + args.mode = SCALING_MODE_CENTER; + } else if (strcmp(argv[3], "tile") == 0) { + args.mode = SCALING_MODE_TILE; + } else if (strcmp(argv[3], "solid_color") == 0) { + args.mode = SCALING_MODE_SOLID_COLOR; + } else { + wlr_log(L_ERROR, "Unsupported scaling mode: %s", argv[3]); + return 1; + } + + state.display = wl_display_connect(NULL); + if (!state.display) { + wlr_log(L_ERROR, "Failed to create display\n"); + return 1; + } + + struct wl_registry *registry = wl_display_get_registry(state.display); + wl_registry_add_listener(registry, ®istry_listener, &state); + wl_display_roundtrip(state.display); + + if (!state.compositor) { + wlr_log(L_DEBUG, "wl-compositor not available"); + return 1; + } + if (!state.layer_shell) { + wlr_log(L_ERROR, "layer-shell not available"); + return 1; + } + + state.surface = wl_compositor_create_surface(state.compositor); + if (!state.surface) { + wlr_log(L_ERROR, "failed to create wl_surface"); + return 1; + } + + state.layer_surface = zwlr_layer_shell_v1_get_layer_surface( + state.layer_shell, state.surface, state.output, + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); + if (!state.layer_surface) { + wlr_log(L_ERROR, "failed to create zwlr_layer_surface"); + return 1; + } + zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0); + zwlr_layer_surface_v1_set_anchor(state.layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); + zwlr_layer_surface_v1_add_listener(state.layer_surface, + &layer_surface_listener, &state); + wl_surface_commit(state.surface); + wl_display_roundtrip(state.display); + + state.run_display = true; + render_frame(&state); + while (wl_display_dispatch(state.display) != -1 && state.run_display) { + // This space intentionally left blank + } return 0; } diff --git a/swaybg/meson.build b/swaybg/meson.build index 47315023..7f5d6bd1 100644 --- a/swaybg/meson.build +++ b/swaybg/meson.build @@ -1,8 +1,22 @@ +deps = [ + cairo, + jsonc, + math, + pango, + pangocairo, + sway_protos, + wayland_client, +] + +if gdk_pixbuf.found() + deps += [gdk_pixbuf] +endif + executable( 'swaybg', 'main.c', include_directories: [sway_inc], - dependencies: [wayland_client, sway_protos, jsonc, wlroots], - link_with: [lib_sway_common], + dependencies: deps, + link_with: [lib_sway_common, lib_sway_client], install: true ) diff --git a/wayland/buffers.c b/wayland/buffers.c index e9780997..3b809e6f 100644 --- a/wayland/buffers.c +++ b/wayland/buffers.c @@ -50,11 +50,8 @@ static const struct wl_buffer_listener buffer_listener = { .release = buffer_release }; -static struct buffer *create_buffer(struct window *window, struct buffer *buf, - int32_t width, int32_t height, int32_t scale, uint32_t format) { - - width *= scale; - height *= scale; +static struct buffer *create_buffer(struct wl_shm *shm, struct buffer *buf, + int32_t width, int32_t height, uint32_t format) { uint32_t stride = width * 4; uint32_t size = stride * height; @@ -65,7 +62,7 @@ static struct buffer *create_buffer(struct window *window, struct buffer *buf, return NULL; // never reached } void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - struct wl_shm_pool *pool = wl_shm_create_pool(window->registry->shm, fd, size); + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); buf->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format); wl_shm_pool_destroy(pool); @@ -101,34 +98,30 @@ static void destroy_buffer(struct buffer *buffer) { memset(buffer, 0, sizeof(struct buffer)); } -struct buffer *get_next_buffer(struct window *window) { +struct pool_buffer *get_next_buffer(struct wl_shm *shm, + struct pool_buffer pool[2], uint32_t width, uint32_t height) { struct buffer *buffer = NULL; - int i; - for (i = 0; i < 2; ++i) { - if (window->buffers[i].busy) { + for (size_t i = 0; i < sizeof(pool) / sizeof(pool[0]); ++i) { + if (buffers[i].busy) { continue; } - buffer = &window->buffers[i]; + buffer = &buffers[i]; } if (!buffer) { return NULL; } - if (buffer->width != window->width || buffer->height != window->height) { + if (buffer->width != width || buffer->height != height) { destroy_buffer(buffer); } if (!buffer->buffer) { - if (!create_buffer(window, buffer, - window->width, window->height, window->scale, + if (!create_buffer(shm, buffer, width, height, WL_SHM_FORMAT_ARGB8888)) { return NULL; } } - - window->cairo = buffer->cairo; - window->buffer = buffer; return buffer; } -- cgit v1.2.3 From d39bda76c4007c42452a81883fefc671b816a74b Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 28 Mar 2018 12:21:50 -0400 Subject: Address review comments --- client/buffer-pool.c | 123 ------------------------------------------------- client/meson.build | 23 ++++------ client/pool-buffer.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++ common/cairo.c | 6 +-- include/buffer_pool.h | 21 --------- include/cairo.h | 3 +- include/meson.build | 1 + include/pool-buffer.h | 21 +++++++++ meson.build | 5 +- swaybg/main.c | 44 ++++++------------ swaybg/meson.build | 36 +++++++-------- 11 files changed, 193 insertions(+), 214 deletions(-) delete mode 100644 client/buffer-pool.c create mode 100644 client/pool-buffer.c delete mode 100644 include/buffer_pool.h create mode 100644 include/meson.build create mode 100644 include/pool-buffer.h (limited to 'meson.build') diff --git a/client/buffer-pool.c b/client/buffer-pool.c deleted file mode 100644 index 7f673ae9..00000000 --- a/client/buffer-pool.c +++ /dev/null @@ -1,123 +0,0 @@ -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "buffer_pool.h" - -static int create_pool_file(size_t size, char **name) { - static const char template[] = "sway-client-XXXXXX"; - const char *path = getenv("XDG_RUNTIME_DIR"); - if (!path) { - return -1; - } - - int ts = (path[strlen(path) - 1] == '/'); - - *name = malloc( - strlen(template) + - strlen(path) + - (ts ? 0 : 1) + 1); - sprintf(*name, "%s%s%s", path, ts ? "" : "/", template); - - int fd = mkstemp(*name); - - if (fd < 0) { - return -1; - } - - if (ftruncate(fd, size) < 0) { - close(fd); - return -1; - } - - return fd; -} - -static void buffer_release(void *data, struct wl_buffer *wl_buffer) { - struct pool_buffer *buffer = data; - buffer->busy = false; -} - -static const struct wl_buffer_listener buffer_listener = { - .release = buffer_release -}; - -static struct pool_buffer *create_buffer(struct wl_shm *shm, - struct pool_buffer *buf, int32_t width, int32_t height, - uint32_t format) { - uint32_t stride = width * 4; - uint32_t size = stride * height; - - char *name; - int fd = create_pool_file(size, &name); - assert(fd); - void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); - buf->buffer = wl_shm_pool_create_buffer(pool, 0, - width, height, stride, format); - wl_shm_pool_destroy(pool); - close(fd); - unlink(name); - free(name); - fd = -1; - - buf->width = width; - buf->height = height; - buf->surface = cairo_image_surface_create_for_data(data, - CAIRO_FORMAT_ARGB32, width, height, stride); - buf->cairo = cairo_create(buf->surface); - buf->pango = pango_cairo_create_context(buf->cairo); - - wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); - return buf; -} - -static void destroy_buffer(struct pool_buffer *buffer) { - if (buffer->buffer) { - wl_buffer_destroy(buffer->buffer); - } - if (buffer->cairo) { - cairo_destroy(buffer->cairo); - } - if (buffer->surface) { - cairo_surface_destroy(buffer->surface); - } - if (buffer->pango) { - g_object_unref(buffer->pango); - } - memset(buffer, 0, sizeof(struct pool_buffer)); -} - -struct pool_buffer *get_next_buffer(struct wl_shm *shm, - struct pool_buffer pool[static 2], uint32_t width, uint32_t height) { - struct pool_buffer *buffer = NULL; - - for (size_t i = 0; i < 2; ++i) { - if (pool[i].busy) { - continue; - } - buffer = &pool[i]; - } - - if (!buffer) { - return NULL; - } - - if (buffer->width != width || buffer->height != height) { - destroy_buffer(buffer); - } - - if (!buffer->buffer) { - if (!create_buffer(shm, buffer, width, height, - WL_SHM_FORMAT_ARGB8888)) { - return NULL; - } - } - return buffer; -} diff --git a/client/meson.build b/client/meson.build index 597899ce..2bdda457 100644 --- a/client/meson.build +++ b/client/meson.build @@ -1,21 +1,16 @@ -deps = [ - cairo, - pango, - pangocairo, - wlroots, - wayland_client, -] - -if gdk_pixbuf.found() - deps += [gdk_pixbuf] -endif - lib_sway_client = static_library( 'sway-client', files( - 'buffer-pool.c', + 'pool-buffer.c', ), - dependencies: deps, + dependencies: [ + cairo, + gdk_pixbuf, + pango, + pangocairo, + wlroots, + wayland_client, + ], link_with: [lib_sway_common], include_directories: sway_inc ) diff --git a/client/pool-buffer.c b/client/pool-buffer.c new file mode 100644 index 00000000..93cfcfc5 --- /dev/null +++ b/client/pool-buffer.c @@ -0,0 +1,124 @@ +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "pool-buffer.h" + +static int create_pool_file(size_t size, char **name) { + static const char template[] = "sway-client-XXXXXX"; + const char *path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + return -1; + } + + int ts = (path[strlen(path) - 1] == '/'); + + *name = malloc( + strlen(template) + + strlen(path) + + (ts ? 0 : 1) + 1); + sprintf(*name, "%s%s%s", path, ts ? "" : "/", template); + + int fd = mkstemp(*name); + + if (fd < 0) { + return -1; + } + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} + +static void buffer_release(void *data, struct wl_buffer *wl_buffer) { + struct pool_buffer *buffer = data; + buffer->busy = false; +} + +static const struct wl_buffer_listener buffer_listener = { + .release = buffer_release +}; + +static struct pool_buffer *create_buffer(struct wl_shm *shm, + struct pool_buffer *buf, int32_t width, int32_t height, + uint32_t format) { + uint32_t stride = width * 4; + uint32_t size = stride * height; + + char *name; + int fd = create_pool_file(size, &name); + assert(fd); + void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); + buf->buffer = wl_shm_pool_create_buffer(pool, 0, + width, height, stride, format); + wl_shm_pool_destroy(pool); + close(fd); + unlink(name); + free(name); + fd = -1; + + buf->width = width; + buf->height = height; + buf->surface = cairo_image_surface_create_for_data(data, + CAIRO_FORMAT_ARGB32, width, height, stride); + buf->cairo = cairo_create(buf->surface); + buf->pango = pango_cairo_create_context(buf->cairo); + + wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); + return buf; +} + +static void destroy_buffer(struct pool_buffer *buffer) { + if (buffer->buffer) { + wl_buffer_destroy(buffer->buffer); + } + if (buffer->cairo) { + cairo_destroy(buffer->cairo); + } + if (buffer->surface) { + cairo_surface_destroy(buffer->surface); + } + if (buffer->pango) { + g_object_unref(buffer->pango); + } + memset(buffer, 0, sizeof(struct pool_buffer)); +} + +struct pool_buffer *get_next_buffer(struct wl_shm *shm, + struct pool_buffer pool[static 2], uint32_t width, uint32_t height) { + struct pool_buffer *buffer = NULL; + + for (size_t i = 0; i < 2; ++i) { + if (pool[i].busy) { + continue; + } + buffer = &pool[i]; + } + + if (!buffer) { + return NULL; + } + + if (buffer->width != width || buffer->height != height) { + destroy_buffer(buffer); + } + + if (!buffer->buffer) { + if (!create_buffer(shm, buffer, width, height, + WL_SHM_FORMAT_ARGB8888)) { + return NULL; + } + } + return buffer; +} diff --git a/common/cairo.c b/common/cairo.c index c6bd0da9..c267c77c 100644 --- a/common/cairo.c +++ b/common/cairo.c @@ -1,7 +1,7 @@ #include #include #include "cairo.h" -#ifdef WITH_GDK_PIXBUF +#ifdef HAVE_GDK_PIXBUF #include #endif @@ -30,7 +30,7 @@ cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, return new; } -#ifdef WITH_GDK_PIXBUF +#ifdef HAVE_GDK_PIXBUF cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { int chan = gdk_pixbuf_get_n_channels(gdkbuf); if (chan < 3) { @@ -124,4 +124,4 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk cairo_surface_mark_dirty(cs); return cs; } -#endif //WITH_GDK_PIXBUF +#endif //HAVE_GDK_PIXBUF diff --git a/include/buffer_pool.h b/include/buffer_pool.h deleted file mode 100644 index cdebd64d..00000000 --- a/include/buffer_pool.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _SWAY_BUFFERS_H -#define _SWAY_BUFFERS_H -#include -#include -#include -#include -#include - -struct pool_buffer { - struct wl_buffer *buffer; - cairo_surface_t *surface; - cairo_t *cairo; - PangoContext *pango; - uint32_t width, height; - bool busy; -}; - -struct pool_buffer *get_next_buffer(struct wl_shm *shm, - struct pool_buffer pool[static 2], uint32_t width, uint32_t height); - -#endif diff --git a/include/cairo.h b/include/cairo.h index f5f474d7..31672705 100644 --- a/include/cairo.h +++ b/include/cairo.h @@ -8,7 +8,8 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color); cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height); -#ifdef WITH_GDK_PIXBUF +#include "config.h" +#ifdef HAVE_GDK_PIXBUF #include cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 00000000..65ed027a --- /dev/null +++ b/include/meson.build @@ -0,0 +1 @@ +configure_file(output: 'config.h', configuration: conf_data) diff --git a/include/pool-buffer.h b/include/pool-buffer.h new file mode 100644 index 00000000..cdebd64d --- /dev/null +++ b/include/pool-buffer.h @@ -0,0 +1,21 @@ +#ifndef _SWAY_BUFFERS_H +#define _SWAY_BUFFERS_H +#include +#include +#include +#include +#include + +struct pool_buffer { + struct wl_buffer *buffer; + cairo_surface_t *surface; + cairo_t *cairo; + PangoContext *pango; + uint32_t width, height; + bool busy; +}; + +struct pool_buffer *get_next_buffer(struct wl_shm *shm, + struct pool_buffer pool[static 2], uint32_t width, uint32_t height); + +#endif diff --git a/meson.build b/meson.build index 0c35b0e5..b681f43a 100644 --- a/meson.build +++ b/meson.build @@ -38,8 +38,10 @@ math = cc.find_library('m') git = find_program('git', required: false) a2x = find_program('a2x', required: false) +conf_data = configuration_data() + if gdk_pixbuf.found() - add_project_arguments('-DWITH_GDK_PIXBUF', language : 'c') + conf_data.set('HAVE_GDK_PIXBUF', true) endif if a2x.found() @@ -92,6 +94,7 @@ add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') sway_inc = include_directories('include') +subdir('include') subdir('protocols') subdir('common') subdir('sway') diff --git a/swaybg/main.c b/swaybg/main.c index 62ddec6c..f431526c 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -6,7 +7,7 @@ #include #include #include -#include "buffer_pool.h" +#include "pool-buffer.h" #include "cairo.h" #include "util.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" @@ -127,7 +128,7 @@ static void render_image(struct swaybg_state *state) { break; } case BACKGROUND_MODE_SOLID_COLOR: - // Should never happen + assert(0); break; } cairo_paint(cairo); @@ -158,7 +159,7 @@ static bool prepare_context(struct swaybg_state *state) { state->context.color = parse_color(state->args->path); return is_valid_color(state->args->path); } -#ifdef WITH_GDK_PIXBUF +#ifdef HAVE_GDK_PIXBUF GError *err = NULL; GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(state->args->path, &err); if (!pixbuf) { @@ -170,17 +171,17 @@ static bool prepare_context(struct swaybg_state *state) { #else state->context.image = cairo_image_surface_create_from_png( state->args->path); -#endif //WITH_GDK_PIXBUF +#endif //HAVE_GDK_PIXBUF if (!state->context.image) { wlr_log(L_ERROR, "Failed to read background image."); return false; } if (cairo_surface_status(state->context.image) != CAIRO_STATUS_SUCCESS) { wlr_log(L_ERROR, "Failed to read background image: %s." -#ifndef WITH_GDK_PIXBUF +#ifndef HAVE_GDK_PIXBUF "\nSway was compiled without gdk_pixbuf support, so only" "\nPNG images can be loaded. This is the likely cause." -#endif //WITH_GDK_PIXBUF +#endif //HAVE_GDK_PIXBUF , cairo_status_to_string( cairo_surface_status(state->context.image))); return false; @@ -256,7 +257,6 @@ int main(int argc, const char **argv) { } args.output_idx = atoi(argv[1]); args.path = argv[2]; - args.mode = atoi(argv[3]); args.mode = BACKGROUND_MODE_STRETCH; if (strcmp(argv[3], "stretch") == 0) { @@ -280,38 +280,20 @@ int main(int argc, const char **argv) { return 1; } - state.display = wl_display_connect(NULL); - if (!state.display) { - wlr_log(L_ERROR, "Failed to create display\n"); - return 1; - } + assert(state.display = wl_display_connect(NULL)); struct wl_registry *registry = wl_display_get_registry(state.display); wl_registry_add_listener(registry, ®istry_listener, &state); wl_display_roundtrip(state.display); + assert(state.compositor && state.layer_shell && state.output && state.shm); - if (!state.compositor) { - wlr_log(L_DEBUG, "wl-compositor not available"); - return 1; - } - if (!state.layer_shell) { - wlr_log(L_ERROR, "layer-shell not available"); - return 1; - } - - state.surface = wl_compositor_create_surface(state.compositor); - if (!state.surface) { - wlr_log(L_ERROR, "failed to create wl_surface"); - return 1; - } + assert(state.surface = wl_compositor_create_surface(state.compositor)); state.layer_surface = zwlr_layer_shell_v1_get_layer_surface( state.layer_shell, state.surface, state.output, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); - if (!state.layer_surface) { - wlr_log(L_ERROR, "failed to create zwlr_layer_surface"); - return 1; - } + assert(state.layer_surface); + zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0); zwlr_layer_surface_v1_set_anchor(state.layer_surface, ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | @@ -320,10 +302,10 @@ int main(int argc, const char **argv) { ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); zwlr_layer_surface_v1_add_listener(state.layer_surface, &layer_surface_listener, &state); + state.run_display = true; wl_surface_commit(state.surface); wl_display_roundtrip(state.display); - state.run_display = true; while (wl_display_dispatch(state.display) != -1 && state.run_display) { // This space intentionally left blank } diff --git a/swaybg/meson.build b/swaybg/meson.build index 7f5d6bd1..5e10f3c7 100644 --- a/swaybg/meson.build +++ b/swaybg/meson.build @@ -1,22 +1,18 @@ -deps = [ - cairo, - jsonc, - math, - pango, - pangocairo, - sway_protos, - wayland_client, -] - -if gdk_pixbuf.found() - deps += [gdk_pixbuf] -endif - executable( - 'swaybg', - 'main.c', - include_directories: [sway_inc], - dependencies: deps, - link_with: [lib_sway_common, lib_sway_client], - install: true + 'swaybg', + 'main.c', + include_directories: [sway_inc], + dependencies: [ + cairo, + gdk_pixbuf, + jsonc, + math, + pango, + pangocairo, + sway_protos, + wayland_client, + wlroots, + ], + link_with: [lib_sway_common, lib_sway_client], + install: true ) -- cgit v1.2.3 From cab1352801b62d1b8a12ca1c995cb24445ce4bc9 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 28 Mar 2018 23:04:20 -0400 Subject: Start port of swaybar to layer shell This starts up the event loop and wayland display and shims out the basic top level rendering concepts. Also includes some changes to incorporate pango into the 1.x codebase properly. --- common/meson.build | 3 + common/pango.c | 67 ++++++ include/pango.h | 16 ++ include/sway/config.h | 7 +- include/swaybar/bar.h | 89 +++----- include/swaybar/config.h | 43 +--- include/swaybar/event_loop.h | 4 +- include/swaybar/ipc.h | 22 +- include/swaybar/render.h | 22 +- meson.build | 3 + swaybar/bar.c | 444 ++++++++---------------------------- swaybar/config.c | 37 ++- swaybar/event_loop.c | 10 +- swaybar/ipc.c | 410 --------------------------------- swaybar/main.c | 33 ++- swaybar/meson.build | 25 ++ swaybar/render.c | 388 ++++--------------------------- swaybar/status_line.c | 530 ------------------------------------------- swaybar/tray/dbus.c | 197 ---------------- swaybar/tray/icon.c | 400 -------------------------------- swaybar/tray/sni.c | 481 --------------------------------------- swaybar/tray/sni_watcher.c | 497 ---------------------------------------- swaybar/tray/tray.c | 398 -------------------------------- 23 files changed, 342 insertions(+), 3784 deletions(-) create mode 100644 common/pango.c create mode 100644 include/pango.h delete mode 100644 swaybar/ipc.c create mode 100644 swaybar/meson.build delete mode 100644 swaybar/status_line.c delete mode 100644 swaybar/tray/dbus.c delete mode 100644 swaybar/tray/icon.c delete mode 100644 swaybar/tray/sni.c delete mode 100644 swaybar/tray/sni_watcher.c delete mode 100644 swaybar/tray/tray.c (limited to 'meson.build') diff --git a/common/meson.build b/common/meson.build index 01736ca6..4ad47077 100644 --- a/common/meson.build +++ b/common/meson.build @@ -1,5 +1,7 @@ deps = [ cairo, + pango, + pangocairo, wlroots ] @@ -14,6 +16,7 @@ lib_sway_common = static_library( 'ipc-client.c', 'log.c', 'list.c', + 'pango.c', 'readline.c', 'stringop.c', 'util.c' diff --git a/common/pango.c b/common/pango.c new file mode 100644 index 00000000..212d96cf --- /dev/null +++ b/common/pango.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, + const char *text, int32_t scale, bool markup) { + PangoLayout *layout = pango_cairo_create_layout(cairo); + PangoAttrList *attrs; + if (markup) { + char *buf; + pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL); + pango_layout_set_markup(layout, buf, -1); + free(buf); + } else { + 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, + int32_t scale, bool markup, const char *fmt, ...) { + char *buf = malloc(2048); + + va_list args; + va_start(args, fmt); + if (vsnprintf(buf, 2048, fmt, args) >= 2048) { + strcpy(buf, "[buffer overflow]"); + } + va_end(args); + + PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); + pango_cairo_update_layout(cairo, layout); + pango_layout_get_pixel_size(layout, width, height); + g_object_unref(layout); + free(buf); +} + +void pango_printf(cairo_t *cairo, const char *font, + int32_t scale, bool markup, const char *fmt, ...) { + char *buf = malloc(2048); + + va_list args; + va_start(args, fmt); + if (vsnprintf(buf, 2048, fmt, args) >= 2048) { + strcpy(buf, "[buffer overflow]"); + } + va_end(args); + + PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); + pango_cairo_update_layout(cairo, layout); + pango_cairo_show_layout(cairo, layout); + g_object_unref(layout); + free(buf); +} diff --git a/include/pango.h b/include/pango.h new file mode 100644 index 00000000..f6325f28 --- /dev/null +++ b/include/pango.h @@ -0,0 +1,16 @@ +#ifndef _SWAY_PANGO_H +#define _SWAY_PANGO_H +#include +#include +#include +#include +#include + +PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, + const char *text, int32_t scale, bool markup); +void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, + int32_t scale, bool markup, const char *fmt, ...); +void pango_printf(cairo_t *cairo, const char *font, + int32_t scale, bool markup, const char *fmt, ...); + +#endif diff --git a/include/sway/config.h b/include/sway/config.h index 48a8b0ab..8c9e04de 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -1,17 +1,16 @@ #ifndef _SWAY_CONFIG_H #define _SWAY_CONFIG_H - #define PID_WORKSPACE_TIMEOUT 60 - #include #include #include +#include #include #include -#include #include "list.h" #include "layout.h" #include "container.h" +#include "wlr-layer-shell-unstable-v1-protocol.h" /** * Describes a variable created via the `set` command. @@ -152,7 +151,7 @@ struct bar_config { char *id; uint32_t modifier; list_t *outputs; - //enum desktop_shell_panel_position position; // TODO + char *position; list_t *bindings; char *status_command; bool pango_markup; diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 50d36e76..3ae8c0b3 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -1,72 +1,45 @@ #ifndef _SWAYBAR_BAR_H #define _SWAYBAR_BAR_H - -#include "client/registry.h" -#include "client/window.h" +#include +#include "pool-buffer.h" #include "list.h" -struct bar { - struct config *config; - struct status_line *status; - list_t *outputs; - struct output *focused_output; +struct swaybar_config; +struct swaybar_output; +struct swaybar_workspace; + +struct swaybar { + struct wl_display *display; + struct wl_compositor *compositor; + struct zwlr_layer_shell_v1 *layer_shell; + struct wl_shm *shm; - int ipc_event_socketfd; - int ipc_socketfd; - int status_read_fd; - int status_write_fd; - pid_t status_command_pid; + struct swaybar_config *config; + struct swaybar_output *focused_output; + + struct wl_list outputs; }; -struct output { - struct window *window; - struct registry *registry; - list_t *workspaces; -#ifdef ENABLE_TRAY - list_t *items; -#endif +struct swaybar_output { + struct wl_list link; + struct swaybar *bar; + struct wl_output *output; + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; + char *name; int idx; bool focused; -}; -struct workspace { - int num; - char *name; - bool focused; - bool visible; - bool urgent; + uint32_t width, height; + struct pool_buffer buffers[2]; + struct pool_buffer *current_buffer; }; -/** Global bar state */ -extern struct bar swaybar; +void bar_setup(struct swaybar *bar, + const char *socket_path, + const char *bar_id); +void bar_run(struct swaybar *bar); +void bar_teardown(struct swaybar *bar); -/** True if sway needs to render */ -extern bool dirty; - -/** - * Setup bar. - */ -void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id); - -/** - * Create new output struct from name. - */ -struct output *new_output(const char *name); - -/** - * Bar mainloop. - */ -void bar_run(struct bar *bar); - -/** - * free workspace list. - */ -void free_workspaces(list_t *workspaces); - -/** - * Teardown bar. - */ -void bar_teardown(struct bar *bar); - -#endif /* _SWAYBAR_BAR_H */ +#endif diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 651f0ee3..1bfe4843 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -1,9 +1,7 @@ #ifndef _SWAYBAR_CONFIG_H #define _SWAYBAR_CONFIG_H - -#include #include - +#include #include "list.h" #include "util.h" @@ -19,10 +17,10 @@ struct box_colors { /** * Swaybar config. */ -struct config { +struct swaybar_config { char *status_command; bool pango_markup; - uint32_t position; + uint32_t position; // zwlr_layer_surface_v1_anchor char *font; char *sep_symbol; char *mode; @@ -32,18 +30,6 @@ struct config { bool workspace_buttons; bool all_outputs; list_t *outputs; - -#ifdef ENABLE_TRAY - // Tray - char *tray_output; - char *icon_theme; - - uint32_t tray_padding; - uint32_t activate_button; - uint32_t context_button; - uint32_t secondary_button; -#endif - int height; struct { @@ -63,24 +49,7 @@ struct config { } colors; }; -/** - * Parse position top|bottom|left|right. - */ -uint32_t parse_position(const char *position); - -/** - * Parse font. - */ -char *parse_font(const char *font); - -/** - * Initialize default sway config. - */ -struct config *init_config(); - -/** - * Free config struct. - */ -void free_config(struct config *config); +struct swaybar_config *init_config(); +void free_config(struct swaybar_config *config); -#endif /* _SWAYBAR_CONFIG_H */ +#endif diff --git a/include/swaybar/event_loop.h b/include/swaybar/event_loop.h index a0cde07f..99f6ed36 100644 --- a/include/swaybar/event_loop.h +++ b/include/swaybar/event_loop.h @@ -1,6 +1,5 @@ #ifndef _SWAYBAR_EVENT_LOOP_H #define _SWAYBAR_EVENT_LOOP_H - #include #include @@ -23,4 +22,5 @@ bool remove_timer(timer_t timer); void event_loop_poll(); void init_event_loop(); -#endif /*_SWAYBAR_EVENT_LOOP_H */ + +#endif diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h index c11931d0..57a1b925 100644 --- a/include/swaybar/ipc.h +++ b/include/swaybar/ipc.h @@ -1,23 +1,9 @@ #ifndef _SWAYBAR_IPC_H #define _SWAYBAR_IPC_H +#include "swaybar/bar.h" -#include "bar.h" - -/** - * Initialize ipc connection to sway and get sway state, outputs, bar_config. - */ -void ipc_bar_init(struct bar *bar, const char *bar_id); - -/** - * Handle ipc event from sway. - */ -bool handle_ipc_event(struct bar *bar); - - -/** - * Send workspace command to sway - */ +void ipc_bar_init(struct swaybar *bar, const char *bar_id); +bool handle_ipc_event(struct swaybar *bar); void ipc_send_workspace_command(const char *workspace_name); -#endif /* _SWAYBAR_IPC_H */ - +#endif diff --git a/include/swaybar/render.h b/include/swaybar/render.h index 114f43f4..071e2298 100644 --- a/include/swaybar/render.h +++ b/include/swaybar/render.h @@ -1,22 +1,10 @@ #ifndef _SWAYBAR_RENDER_H #define _SWAYBAR_RENDER_H -#include "config.h" -#include "bar.h" +struct swaybar; +struct swaybar_output; +struct swaybar_config; -/** - * Render swaybar. - */ -void render(struct output *output, struct config *config, struct status_line *line); +void render_frame(struct swaybar *bar, struct swaybar_output *output); -/** - * Set window height and modify internal spacing accordingly. - */ -void set_window_height(struct window *window, int height); - -/** - * Compute the size of a workspace name - */ -void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height); - -#endif /* _SWAYBAR_RENDER_H */ +#endif diff --git a/meson.build b/meson.build index b681f43a..49824b30 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,7 @@ pixman = dependency('pixman-1') libcap = dependency('libcap') libinput = dependency('libinput') math = cc.find_library('m') +rt = cc.find_library('rt') git = find_program('git', required: false) a2x = find_program('a2x', required: false) @@ -99,8 +100,10 @@ subdir('protocols') subdir('common') subdir('sway') subdir('swaymsg') + subdir('client') subdir('swaybg') +subdir('swaybar') config = configuration_data() config.set('sysconfdir', join_paths(prefix, sysconfdir)) diff --git a/swaybar/bar.c b/swaybar/bar.c index f12923a8..e1d594b4 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -1,390 +1,146 @@ #define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include #include -#include #include -#include -#include #include -#include -#include -#ifdef __FreeBSD__ -#include -#else -#include -#endif -#ifdef ENABLE_TRAY -#include -#include "swaybar/tray/sni_watcher.h" -#include "swaybar/tray/tray.h" -#include "swaybar/tray/sni.h" -#endif -#include "swaybar/ipc.h" +#include +#include +#include #include "swaybar/render.h" #include "swaybar/config.h" -#include "swaybar/status_line.h" #include "swaybar/event_loop.h" #include "swaybar/bar.h" -#include "ipc-client.h" #include "list.h" -#include "log.h" +#include "pango.h" +#include "pool-buffer.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" -static void bar_init(struct bar *bar) { +static void bar_init(struct swaybar *bar) { bar->config = init_config(); - bar->status = init_status_line(); - bar->outputs = create_list(); + wl_list_init(&bar->outputs); } -static void spawn_status_cmd_proc(struct bar *bar) { - if (bar->config->status_command) { - int pipe_read_fd[2]; - int pipe_write_fd[2]; - - if (pipe(pipe_read_fd) != 0) { - sway_log(L_ERROR, "Unable to create pipes for status_command fork"); - return; - } - if (pipe(pipe_write_fd) != 0) { - sway_log(L_ERROR, "Unable to create pipe for status_command fork (write)"); - close(pipe_read_fd[0]); - close(pipe_read_fd[1]); - return; - } - - bar->status_command_pid = fork(); - if (bar->status_command_pid == 0) { - close(pipe_read_fd[0]); - dup2(pipe_read_fd[1], STDOUT_FILENO); - close(pipe_read_fd[1]); - - dup2(pipe_write_fd[0], STDIN_FILENO); - close(pipe_write_fd[0]); - close(pipe_write_fd[1]); - - char *const cmd[] = { - "sh", - "-c", - bar->config->status_command, - NULL, - }; - execvp(cmd[0], cmd); - return; - } - - close(pipe_read_fd[1]); - bar->status_read_fd = pipe_read_fd[0]; - fcntl(bar->status_read_fd, F_SETFL, O_NONBLOCK); - - close(pipe_write_fd[0]); - bar->status_write_fd = pipe_write_fd[1]; - fcntl(bar->status_write_fd, F_SETFL, O_NONBLOCK); - } -} - -struct output *new_output(const char *name) { - struct output *output = malloc(sizeof(struct output)); +struct swaybar_output *new_output(const char *name) { + struct swaybar_output *output = malloc(sizeof(struct swaybar_output)); output->name = strdup(name); - output->window = NULL; - output->registry = NULL; - output->workspaces = create_list(); -#ifdef ENABLE_TRAY - output->items = create_list(); -#endif return output; } -static void mouse_button_notify(struct window *window, int x, int y, - uint32_t button, uint32_t state_w) { - sway_log(L_DEBUG, "Mouse button %d clicked at %d %d %d", button, x, y, state_w); - if (!state_w) { - return; - } - - struct output *clicked_output = NULL; - for (int i = 0; i < swaybar.outputs->length; i++) { - struct output *output = swaybar.outputs->items[i]; - if (window == output->window) { - clicked_output = output; - break; - } - } - - if (!sway_assert(clicked_output != NULL, "Got pointer event for non-existing output")) { - return; - } - - double button_x = 0.5; - for (int i = 0; i < clicked_output->workspaces->length; i++) { - struct workspace *workspace = clicked_output->workspaces->items[i]; - int button_width, button_height; - - workspace_button_size(window, workspace->name, &button_width, &button_height); - - button_x += button_width; - if (x <= button_x) { - ipc_send_workspace_command(workspace->name); - break; - } - } - - switch (button) { - case BTN_LEFT: - status_line_mouse_event(&swaybar, x, y, 1); - break; - case BTN_MIDDLE: - status_line_mouse_event(&swaybar, x, y, 2); - break; - case BTN_RIGHT: - status_line_mouse_event(&swaybar, x, y, 3); - break; - } - -#ifdef ENABLE_TRAY - tray_mouse_event(clicked_output, x, y, button, state_w); -#endif - +static void layer_surface_configure(void *data, + struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t width, uint32_t height) { + struct swaybar_output *output = data; + output->width = width; + output->height = height; + zwlr_layer_surface_v1_ack_configure(surface, serial); + render_frame(output->bar, output); } -static void mouse_scroll_notify(struct window *window, enum scroll_direction direction) { - sway_log(L_DEBUG, "Mouse wheel scrolled %s", direction == SCROLL_UP ? "up" : "down"); - - // If there are status blocks and click_events are enabled - // check if the position is within the status area and if so - // tell the status line to output the event and skip workspace - // switching below. - int num_blocks = swaybar.status->block_line->length; - if (swaybar.status->click_events && num_blocks > 0) { - struct status_block *first_block = swaybar.status->block_line->items[0]; - int x = window->pointer_input.last_x; - int y = window->pointer_input.last_y; - if (x > first_block->x) { - if (direction == SCROLL_UP) { - status_line_mouse_event(&swaybar, x, y, 4); - } else { - status_line_mouse_event(&swaybar, x, y, 5); - } - return; - } - } +static void layer_surface_closed(void *_output, + struct zwlr_layer_surface_v1 *surface) { + // TODO: Deal with hotplugging + struct swaybar_output *output = output; + zwlr_layer_surface_v1_destroy(output->layer_surface); + wl_surface_destroy(output->surface); +} - if (!swaybar.config->wrap_scroll) { - // Find output this window lives on - int i; - struct output *output = NULL; - for (i = 0; i < swaybar.outputs->length; ++i) { - output = swaybar.outputs->items[i]; - if (output->window == window) { - break; - } - } - if (!sway_assert(i != swaybar.outputs->length, "Unknown window in scroll event")) { - return; - } - int focused = -1; - for (i = 0; i < output->workspaces->length; ++i) { - struct workspace *ws = output->workspaces->items[i]; - if (ws->focused) { - focused = i; - break; - } - } - if (!sway_assert(focused != -1, "Scroll wheel event received on inactive output")) { - return; - } - if ((focused == 0 && direction == SCROLL_UP) || - (focused == output->workspaces->length - 1 && direction == SCROLL_DOWN)) { - // Do not wrap - return; - } +struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_configure, + .closed = layer_surface_closed, +}; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + struct swaybar *bar = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + bar->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 1); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + bar->shm = wl_registry_bind(registry, name, + &wl_shm_interface, 1); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + static int idx = 0; + struct swaybar_output *output = + calloc(1, sizeof(struct swaybar_output)); + output->bar = bar; + output->output = wl_registry_bind(registry, name, + &wl_output_interface, 1); + output->idx = idx++; + wl_list_insert(&bar->outputs, &output->link); + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + bar->layer_shell = wl_registry_bind( + registry, name, &zwlr_layer_shell_v1_interface, 1); } +} - const char *workspace_name = direction == SCROLL_UP ? "prev_on_output" : "next_on_output"; - ipc_send_workspace_command(workspace_name); +static void handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) { + // who cares } -void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) { - /* initialize bar with default values */ - bar_init(bar); +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; - /* Initialize event loop lists */ +void bar_setup(struct swaybar *bar, + const char *socket_path, const char *bar_id) { + bar_init(bar); init_event_loop(); - /* connect to sway ipc */ - bar->ipc_socketfd = ipc_open_socket(socket_path); - bar->ipc_event_socketfd = ipc_open_socket(socket_path); - - ipc_bar_init(bar, bar_id); - - int i; - for (i = 0; i < bar->outputs->length; ++i) { - struct output *bar_output = bar->outputs->items[i]; - - bar_output->registry = registry_poll(); - - if (!bar_output->registry->desktop_shell) { - sway_abort("swaybar requires the compositor to support the desktop-shell extension."); - } - - struct output_state *output = bar_output->registry->outputs->items[bar_output->idx]; - - bar_output->window = window_setup(bar_output->registry, - output->width / output->scale, 30, output->scale, false); - if (!bar_output->window) { - sway_abort("Failed to create window."); - } - desktop_shell_set_panel(bar_output->registry->desktop_shell, - output->output, bar_output->window->surface); - desktop_shell_set_panel_position(bar_output->registry->desktop_shell, + assert(bar->display = wl_display_connect(NULL)); + + struct wl_registry *registry = wl_display_get_registry(bar->display); + wl_registry_add_listener(registry, ®istry_listener, bar); + wl_display_roundtrip(bar->display); + assert(bar->compositor && bar->layer_shell && bar->shm); + + // TODO: we might not necessarily be meant to do all of the outputs + struct swaybar_output *output; + wl_list_for_each(output, &bar->outputs, link) { + assert(output->surface = wl_compositor_create_surface(bar->compositor)); + output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + bar->layer_shell, output->surface, output->output, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); + assert(output->layer_surface); + zwlr_layer_surface_v1_add_listener(output->layer_surface, + &layer_surface_listener, output); + zwlr_layer_surface_v1_set_anchor(output->layer_surface, bar->config->position); - - window_make_shell(bar_output->window); - - /* set font */ - bar_output->window->font = bar->config->font; - - /* set mouse event callbacks */ - bar_output->window->pointer_input.notify_button = mouse_button_notify; - bar_output->window->pointer_input.notify_scroll = mouse_scroll_notify; - - /* set window height */ - set_window_height(bar_output->window, bar->config->height); + render_frame(bar, output); } - /* spawn status command */ - spawn_status_cmd_proc(bar); - -#ifdef ENABLE_TRAY - init_tray(bar); -#endif } -bool dirty = true; - -static void respond_ipc(int fd, short mask, void *_bar) { - struct bar *bar = (struct bar *)_bar; - sway_log(L_DEBUG, "Got IPC event."); - dirty = handle_ipc_event(bar); -} - -static void respond_command(int fd, short mask, void *_bar) { - struct bar *bar = (struct bar *)_bar; - dirty = handle_status_line(bar); -} - -static void respond_output(int fd, short mask, void *_output) { - struct output *output = (struct output *)_output; - if (wl_display_dispatch(output->registry->display) == -1) { - sway_log(L_ERROR, "failed to dispatch wl: %d", errno); +static void display_in(int fd, short mask, void *_bar) { + struct swaybar *bar = (struct swaybar *)_bar; + if (wl_display_dispatch(bar->display) == -1) { + wlr_log(L_ERROR, "failed to dispatch wl: %d", errno); } } -void bar_run(struct bar *bar) { - add_event(bar->ipc_event_socketfd, POLLIN, respond_ipc, bar); - add_event(bar->status_read_fd, POLLIN, respond_command, bar); - - int i; - for (i = 0; i < bar->outputs->length; ++i) { - struct output *output = bar->outputs->items[i]; - add_event(wl_display_get_fd(output->registry->display), - POLLIN, respond_output, output); - } - +void bar_run(struct swaybar *bar) { + add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar); while (1) { - if (dirty) { - int i; - for (i = 0; i < bar->outputs->length; ++i) { - struct output *output = bar->outputs->items[i]; - if (window_prerender(output->window) && output->window->cairo) { - render(output, bar->config, bar->status); - window_render(output->window); - wl_display_flush(output->registry->display); - } - } - } - - dirty = false; - event_loop_poll(); -#ifdef ENABLE_TRAY - dispatch_dbus(); -#endif } } -void free_workspaces(list_t *workspaces) { - int i; - for (i = 0; i < workspaces->length; ++i) { - struct workspace *ws = workspaces->items[i]; - free(ws->name); - free(ws); +static void free_outputs(struct wl_list *list) { + struct swaybar_output *output, *tmp; + wl_list_for_each_safe(output, tmp, list, link) { + wl_list_remove(&output->link); + free(output->name); + free(output); } - list_free(workspaces); } -static void free_output(struct output *output) { - window_teardown(output->window); - if (output->registry) { - registry_teardown(output->registry); - } - - free(output->name); - - if (output->workspaces) { - free_workspaces(output->workspaces); - } - - free(output); -} - -static void free_outputs(list_t *outputs) { - int i; - for (i = 0; i < outputs->length; ++i) { - free_output(outputs->items[i]); - } - list_free(outputs); -} - -static void terminate_status_command(pid_t pid) { - if (pid) { - // terminate status_command process - int ret = kill(pid, SIGTERM); - if (ret != 0) { - sway_log(L_ERROR, "Unable to terminate status_command [pid: %d]", pid); - } else { - int status; - waitpid(pid, &status, 0); - } - } -} - -void bar_teardown(struct bar *bar) { +void bar_teardown(struct swaybar *bar) { + free_outputs(&bar->outputs); if (bar->config) { free_config(bar->config); } - - if (bar->outputs) { - free_outputs(bar->outputs); - } - - if (bar->status) { - free_status_line(bar->status); - } - - /* close sockets/pipes */ - if (bar->status_read_fd) { - close(bar->status_read_fd); - } - - if (bar->status_write_fd) { - close(bar->status_write_fd); - } - - if (bar->ipc_socketfd) { - close(bar->ipc_socketfd); - } - - if (bar->ipc_event_socketfd) { - close(bar->ipc_event_socketfd); - } - - /* terminate status command process */ - terminate_status_command(bar->status_command_pid); } diff --git a/swaybar/config.c b/swaybar/config.c index 8fe552f2..0c2b57e0 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -1,21 +1,24 @@ #define _XOPEN_SOURCE 500 #include #include -#include "wayland-desktop-shell-client-protocol.h" -#include "log.h" #include "swaybar/config.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" uint32_t parse_position(const char *position) { + uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; if (strcmp("top", position) == 0) { - return DESKTOP_SHELL_PANEL_POSITION_TOP; + return ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | horiz; } else if (strcmp("bottom", position) == 0) { - return DESKTOP_SHELL_PANEL_POSITION_BOTTOM; + return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz; } else if (strcmp("left", position) == 0) { - return DESKTOP_SHELL_PANEL_POSITION_LEFT; + return ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | vert; } else if (strcmp("right", position) == 0) { - return DESKTOP_SHELL_PANEL_POSITION_RIGHT; + return ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | vert; } else { - return DESKTOP_SHELL_PANEL_POSITION_BOTTOM; + return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz; } } @@ -30,11 +33,11 @@ char *parse_font(const char *font) { return new_font; } -struct config *init_config() { - struct config *config = calloc(1, sizeof(struct config)); +struct swaybar_config *init_config() { + struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config)); config->status_command = NULL; config->pango_markup = false; - config->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM; + config->position = parse_position("bottom"); config->font = strdup("monospace 10"); config->mode = NULL; config->sep_symbol = NULL; @@ -48,18 +51,6 @@ struct config *init_config() { /* height */ config->height = 0; -#ifdef ENABLE_TRAY - config->tray_output = NULL; - config->icon_theme = NULL; - config->tray_padding = 2; - /** - * These constants are used by wayland and are defined in - * linux/input-event-codes.h - */ - config->activate_button = 0x110; /* BTN_LEFT */ - config->context_button = 0x111; /* BTN_RIGHT */ -#endif - /* colors */ config->colors.background = 0x000000FF; config->colors.statusline = 0xFFFFFFFF; @@ -88,7 +79,7 @@ struct config *init_config() { return config; } -void free_config(struct config *config) { +void free_config(struct swaybar_config *config) { free(config->status_command); free(config->font); free(config->mode); diff --git a/swaybar/event_loop.c b/swaybar/event_loop.c index 0d1be1da..748372ed 100644 --- a/swaybar/event_loop.c +++ b/swaybar/event_loop.c @@ -4,19 +4,18 @@ #include #include #include -#include "swaybar/bar.h" +#include #include "swaybar/event_loop.h" #include "list.h" -#include "log.h" struct event_item { - void(*cb)(int fd, short mask, void *data); + void (*cb)(int fd, short mask, void *data); void *data; }; struct timer_item { timer_t timer; - void(*cb)(timer_t timer, void *data); + void (*cb)(timer_t timer, void *data); void *data; }; @@ -138,7 +137,8 @@ void event_loop_poll() { void init_event_loop() { event_loop.fds.length = 0; event_loop.fds.capacity = 10; - event_loop.fds.items = malloc(event_loop.fds.capacity * sizeof(struct pollfd)); + event_loop.fds.items = malloc( + event_loop.fds.capacity * sizeof(struct pollfd)); event_loop.items = create_list(); event_loop.timers = create_list(); } diff --git a/swaybar/ipc.c b/swaybar/ipc.c deleted file mode 100644 index 93d1219c..00000000 --- a/swaybar/ipc.c +++ /dev/null @@ -1,410 +0,0 @@ -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include "swaybar/config.h" -#include "swaybar/ipc.h" -#include "ipc-client.h" -#include "list.h" -#include "log.h" - -void ipc_send_workspace_command(const char *workspace_name) { - uint32_t size = strlen("workspace \"\"") + strlen(workspace_name) + 1; - - char command[size]; - sprintf(command, "workspace \"%s\"", workspace_name); - - ipc_single_command(swaybar.ipc_socketfd, IPC_COMMAND, command, &size); -} - -static void ipc_parse_config(struct config *config, const char *payload) { - json_object *bar_config = json_tokener_parse(payload); - json_object *markup, *mode, *hidden_bar, *position, *status_command; - json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; - json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; -#ifdef ENABLE_TRAY - json_object *tray_output, *icon_theme, *tray_padding, *activate_button, *context_button; - json_object *secondary_button; - json_object_object_get_ex(bar_config, "tray_output", &tray_output); - json_object_object_get_ex(bar_config, "icon_theme", &icon_theme); - json_object_object_get_ex(bar_config, "tray_padding", &tray_padding); - json_object_object_get_ex(bar_config, "activate_button", &activate_button); - json_object_object_get_ex(bar_config, "context_button", &context_button); - json_object_object_get_ex(bar_config, "secondary_button", &secondary_button); -#endif - json_object_object_get_ex(bar_config, "mode", &mode); - json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar); - json_object_object_get_ex(bar_config, "position", &position); - json_object_object_get_ex(bar_config, "status_command", &status_command); - json_object_object_get_ex(bar_config, "font", &font); - json_object_object_get_ex(bar_config, "bar_height", &bar_height); - json_object_object_get_ex(bar_config, "wrap_scroll", &wrap_scroll); - json_object_object_get_ex(bar_config, "workspace_buttons", &workspace_buttons); - json_object_object_get_ex(bar_config, "strip_workspace_numbers", &strip_workspace_numbers); - json_object_object_get_ex(bar_config, "binding_mode_indicator", &binding_mode_indicator); - json_object_object_get_ex(bar_config, "verbose", &verbose); - json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol); - json_object_object_get_ex(bar_config, "colors", &colors); - json_object_object_get_ex(bar_config, "outputs", &outputs); - json_object_object_get_ex(bar_config, "pango_markup", &markup); - - if (status_command) { - free(config->status_command); - config->status_command = strdup(json_object_get_string(status_command)); - } - - if (position) { - config->position = parse_position(json_object_get_string(position)); - } - - if (font) { - free(config->font); - config->font = parse_font(json_object_get_string(font)); - } - - if (sep_symbol) { - free(config->sep_symbol); - config->sep_symbol = strdup(json_object_get_string(sep_symbol)); - } - - if (strip_workspace_numbers) { - config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers); - } - - if (binding_mode_indicator) { - config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator); - } - - if (wrap_scroll) { - config->wrap_scroll = json_object_get_boolean(wrap_scroll); - } - - if (workspace_buttons) { - config->workspace_buttons = json_object_get_boolean(workspace_buttons); - } - - if (bar_height) { - config->height = json_object_get_int(bar_height); - } - - if (markup) { - config->pango_markup = json_object_get_boolean(markup); - } - -#ifdef ENABLE_TRAY - if (tray_output) { - free(config->tray_output); - config->tray_output = strdup(json_object_get_string(tray_output)); - } - - if (icon_theme) { - free(config->icon_theme); - config->icon_theme = strdup(json_object_get_string(icon_theme)); - } - - if (tray_padding) { - config->tray_padding = json_object_get_int(tray_padding); - } - - if (activate_button) { - config->activate_button = json_object_get_int(activate_button); - } - - if (context_button) { - config->context_button = json_object_get_int(context_button); - } - - if (secondary_button) { - config->secondary_button = json_object_get_int(secondary_button); - } -#endif - - // free previous outputs list - int i; - for (i = 0; i < config->outputs->length; ++i) { - free(config->outputs->items[i]); - } - list_free(config->outputs); - config->outputs = create_list(); - - if (outputs) { - int length = json_object_array_length(outputs); - json_object *output; - const char *output_str; - for (i = 0; i < length; ++i) { - output = json_object_array_get_idx(outputs, i); - output_str = json_object_get_string(output); - if (strcmp("*", output_str) == 0) { - config->all_outputs = true; - break; - } - list_add(config->outputs, strdup(output_str)); - } - } else { - config->all_outputs = true; - } - - if (colors) { - json_object *background, *statusline, *separator; - json_object *focused_background, *focused_statusline, *focused_separator; - json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text; - json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text; - json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text; - json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text; - json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text; - json_object_object_get_ex(colors, "background", &background); - json_object_object_get_ex(colors, "statusline", &statusline); - json_object_object_get_ex(colors, "separator", &separator); - json_object_object_get_ex(colors, "focused_background", &focused_background); - json_object_object_get_ex(colors, "focused_statusline", &focused_statusline); - json_object_object_get_ex(colors, "focused_separator", &focused_separator); - json_object_object_get_ex(colors, "focused_workspace_border", &focused_workspace_border); - json_object_object_get_ex(colors, "focused_workspace_bg", &focused_workspace_bg); - json_object_object_get_ex(colors, "focused_workspace_text", &focused_workspace_text); - json_object_object_get_ex(colors, "active_workspace_border", &active_workspace_border); - json_object_object_get_ex(colors, "active_workspace_bg", &active_workspace_bg); - json_object_object_get_ex(colors, "active_workspace_text", &active_workspace_text); - json_object_object_get_ex(colors, "inactive_workspace_border", &inactive_workspace_border); - json_object_object_get_ex(colors, "inactive_workspace_bg", &inactive_workspace_bg); - json_object_object_get_ex(colors, "inactive_workspace_text", &inactive_workspace_text); - json_object_object_get_ex(colors, "urgent_workspace_border", &urgent_workspace_border); - json_object_object_get_ex(colors, "urgent_workspace_bg", &urgent_workspace_bg); - json_object_object_get_ex(colors, "urgent_workspace_text", &urgent_workspace_text); - json_object_object_get_ex(colors, "binding_mode_border", &binding_mode_border); - json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg); - json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text); - if (background) { - config->colors.background = parse_color(json_object_get_string(background)); - } - - if (statusline) { - config->colors.statusline = parse_color(json_object_get_string(statusline)); - } - - if (separator) { - config->colors.separator = parse_color(json_object_get_string(separator)); - } - - if (focused_background) { - config->colors.focused_background = parse_color(json_object_get_string(focused_background)); - } - - if (focused_statusline) { - config->colors.focused_statusline = parse_color(json_object_get_string(focused_statusline)); - } - - if (focused_separator) { - config->colors.focused_separator = parse_color(json_object_get_string(focused_separator)); - } - - if (focused_workspace_border) { - config->colors.focused_workspace.border = parse_color(json_object_get_string(focused_workspace_border)); - } - - if (focused_workspace_bg) { - config->colors.focused_workspace.background = parse_color(json_object_get_string(focused_workspace_bg)); - } - - if (focused_workspace_text) { - config->colors.focused_workspace.text = parse_color(json_object_get_string(focused_workspace_text)); - } - - if (active_workspace_border) { - config->colors.active_workspace.border = parse_color(json_object_get_string(active_workspace_border)); - } - - if (active_workspace_bg) { - config->colors.active_workspace.background = parse_color(json_object_get_string(active_workspace_bg)); - } - - if (active_workspace_text) { - config->colors.active_workspace.text = parse_color(json_object_get_string(active_workspace_text)); - } - - if (inactive_workspace_border) { - config->colors.inactive_workspace.border = parse_color(json_object_get_string(inactive_workspace_border)); - } - - if (inactive_workspace_bg) { - config->colors.inactive_workspace.background = parse_color(json_object_get_string(inactive_workspace_bg)); - } - - if (inactive_workspace_text) { - config->colors.inactive_workspace.text = parse_color(json_object_get_string(inactive_workspace_text)); - } - - if (binding_mode_border) { - config->colors.binding_mode.border = parse_color(json_object_get_string(binding_mode_border)); - } - - if (binding_mode_bg) { - config->colors.binding_mode.background = parse_color(json_object_get_string(binding_mode_bg)); - } - - if (binding_mode_text) { - config->colors.binding_mode.text = parse_color(json_object_get_string(binding_mode_text)); - } - } - - json_object_put(bar_config); -} - -static void ipc_update_workspaces(struct bar *bar) { - int i; - for (i = 0; i < bar->outputs->length; ++i) { - struct output *output = bar->outputs->items[i]; - if (output->workspaces) { - free_workspaces(output->workspaces); - } - output->workspaces = create_list(); - } - - uint32_t len = 0; - char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_WORKSPACES, NULL, &len); - json_object *results = json_tokener_parse(res); - if (!results) { - free(res); - return; - } - - int length = json_object_array_length(results); - json_object *ws_json; - json_object *num, *name, *visible, *focused, *out, *urgent; - for (i = 0; i < length; ++i) { - ws_json = json_object_array_get_idx(results, i); - - json_object_object_get_ex(ws_json, "num", &num); - json_object_object_get_ex(ws_json, "name", &name); - json_object_object_get_ex(ws_json, "visible", &visible); - json_object_object_get_ex(ws_json, "focused", &focused); - json_object_object_get_ex(ws_json, "output", &out); - json_object_object_get_ex(ws_json, "urgent", &urgent); - - int j; - for (j = 0; j < bar->outputs->length; ++j) { - struct output *output = bar->outputs->items[j]; - if (strcmp(json_object_get_string(out), output->name) == 0) { - struct workspace *ws = malloc(sizeof(struct workspace)); - ws->num = json_object_get_int(num); - ws->name = strdup(json_object_get_string(name)); - ws->visible = json_object_get_boolean(visible); - ws->focused = json_object_get_boolean(focused); - if (ws->focused) { - if (bar->focused_output) { - bar->focused_output->focused = false; - } - bar->focused_output = output; - output->focused = true; - } - ws->urgent = json_object_get_boolean(urgent); - list_add(output->workspaces, ws); - } - } - } - - json_object_put(results); - free(res); -} - -void ipc_bar_init(struct bar *bar, const char *bar_id) { - // Get bar config - uint32_t len = strlen(bar_id); - char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len); - - ipc_parse_config(bar->config, res); - free(res); - - // Get outputs - len = 0; - res = ipc_single_command(bar->ipc_socketfd, IPC_GET_OUTPUTS, NULL, &len); - json_object *outputs = json_tokener_parse(res); - int i; - int length = json_object_array_length(outputs); - json_object *output, *output_name, *output_active; - const char *name; - bool active; - for (i = 0; i < length; ++i) { - output = json_object_array_get_idx(outputs, i); - json_object_object_get_ex(output, "name", &output_name); - json_object_object_get_ex(output, "active", &output_active); - name = json_object_get_string(output_name); - active = json_object_get_boolean(output_active); - if (!active) { - continue; - } - - bool use_output = false; - if (bar->config->all_outputs) { - use_output = true; - } else { - int j = 0; - for (j = 0; j < bar->config->outputs->length; ++j) { - const char *conf_name = bar->config->outputs->items[j]; - if (strcasecmp(name, conf_name) == 0) { - use_output = true; - break; - } - } - } - - if (!use_output) { - continue; - } - - // add bar to the output - struct output *bar_output = new_output(name); - bar_output->idx = i; - list_add(bar->outputs, bar_output); - } - free(res); - json_object_put(outputs); - - const char *subscribe_json = "[ \"workspace\", \"mode\" ]"; - len = strlen(subscribe_json); - res = ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe_json, &len); - free(res); - - ipc_update_workspaces(bar); -} - -bool handle_ipc_event(struct bar *bar) { - struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd); - if (!resp) { - return false; - } - switch (resp->type) { - case IPC_EVENT_WORKSPACE: - ipc_update_workspaces(bar); - break; - case IPC_EVENT_MODE: { - json_object *result = json_tokener_parse(resp->payload); - if (!result) { - free_ipc_response(resp); - sway_log(L_ERROR, "failed to parse payload as json"); - return false; - } - json_object *json_change; - if (json_object_object_get_ex(result, "change", &json_change)) { - const char *change = json_object_get_string(json_change); - - free(bar->config->mode); - if (strcmp(change, "default") == 0) { - bar->config->mode = NULL; - } else { - bar->config->mode = strdup(change); - } - } else { - sway_log(L_ERROR, "failed to parse response"); - } - - json_object_put(result); - break; - } - default: - free_ipc_response(resp); - return false; - } - - free_ipc_response(resp); - return true; -} diff --git a/swaybar/main.c b/swaybar/main.c index 0abd0755..c897e1c9 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -4,21 +4,20 @@ #include #include #include +#include #include "swaybar/bar.h" #include "ipc-client.h" -#include "log.h" -/* global bar state */ -struct bar swaybar; +static struct swaybar swaybar; -void sway_terminate(int exit_code) { +void sig_handler(int signal) { bar_teardown(&swaybar); - exit(exit_code); + exit(0); } -void sig_handler(int signal) { +void sway_terminate(int code) { bar_teardown(&swaybar); - exit(0); + exit(code); } int main(int argc, char **argv) { @@ -75,20 +74,23 @@ int main(int argc, char **argv) { } } - if (!bar_id) { - sway_abort("No bar_id passed. Provide --bar_id or let sway start swaybar"); - } - if (debug) { - init_log(L_DEBUG); + wlr_log_init(L_DEBUG, NULL); } else { - init_log(L_ERROR); + wlr_log_init(L_ERROR, NULL); + } + + if (!bar_id) { + wlr_log(L_ERROR, "No bar_id passed. " + "Provide --bar_id or let sway start swaybar"); + return 1; } if (!socket_path) { socket_path = get_socketpath(); if (!socket_path) { - sway_abort("Unable to retrieve socket path"); + wlr_log(L_ERROR, "Unable to retrieve socket path"); + return 1; } } @@ -100,9 +102,6 @@ int main(int argc, char **argv) { free(bar_id); bar_run(&swaybar); - - // gracefully shutdown swaybar and status_command bar_teardown(&swaybar); - return 0; } diff --git a/swaybar/meson.build b/swaybar/meson.build new file mode 100644 index 00000000..fd87e51d --- /dev/null +++ b/swaybar/meson.build @@ -0,0 +1,25 @@ +executable( + 'swaybar', + [ + 'bar.c', + 'config.c', + 'event_loop.c', + 'main.c', + 'render.c', + ], + include_directories: [sway_inc], + dependencies: [ + cairo, + client_protos, + gdk_pixbuf, + jsonc, + math, + pango, + pangocairo, + rt, + wayland_client, + wlroots, + ], + link_with: [lib_sway_common, lib_sway_client], + install: true +) diff --git a/swaybar/render.c b/swaybar/render.c index 6fc09078..2eaa0195 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -1,367 +1,63 @@ #include #include #include - -#include "client/cairo.h" -#include "client/pango.h" -#include "client/window.h" +#include +#include "cairo.h" +#include "pango.h" +#include "pool-buffer.h" +#include "swaybar/bar.h" #include "swaybar/config.h" -#include "swaybar/status_line.h" #include "swaybar/render.h" -#ifdef ENABLE_TRAY -#include "swaybar/tray/tray.h" -#include "swaybar/tray/sni.h" -#endif -#include "log.h" - - -/* internal spacing */ -static int margin = 3; -static int ws_horizontal_padding = 5; -static double ws_vertical_padding = 1.5; -static int ws_spacing = 1; - -/** - * Renders a sharp line of any width and height. - * - * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0 - * if the line has a width/height of one pixel, respectively. - */ -static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) { - cairo_set_source_u32(cairo, color); - - if (width > 1 && height > 1) { - cairo_rectangle(cairo, x, y, width, height); - cairo_fill(cairo); - } else { - if (width == 1) { - x += 0.5; - height += y; - width = x; - } - - if (height == 1) { - y += 0.5; - width += x; - height = y; - } - - cairo_move_to(cairo, x, y); - cairo_set_line_width(cairo, 1.0); - cairo_line_to(cairo, width, height); - cairo_stroke(cairo); - } -} - -static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) { - int width, height, sep_width; - get_text_size(window->cairo, window->font, &width, &height, - window->scale, block->markup, "%s", block->full_text); - - int textwidth = width; - double block_width = width; - - if (width < block->min_width) { - width = block->min_width; - } - - *x -= width; - - if (block->border != 0 && block->border_left > 0) { - *x -= (block->border_left + margin); - block_width += block->border_left + margin; - } - - if (block->border != 0 && block->border_right > 0) { - *x -= (block->border_right + margin); - block_width += block->border_right + margin; - } - - // Add separator - if (!edge) { - if (config->sep_symbol) { - get_text_size(window->cairo, window->font, &sep_width, &height, - window->scale, false, "%s", config->sep_symbol); - if (sep_width > block->separator_block_width) { - block->separator_block_width = sep_width + margin * 2; - } - } - - *x -= block->separator_block_width; - } else { - *x -= margin; - } - - double pos = *x; - - block->x = (int)pos; - block->width = (int)block_width; - - // render background - if (block->background != 0x0) { - cairo_set_source_u32(window->cairo, block->background); - cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, (window->height * window->scale) - 2); - cairo_fill(window->cairo); - } - - // render top border - if (block->border != 0 && block->border_top > 0) { - render_sharp_line(window->cairo, block->border, - pos - 0.5, - 1, - block_width, - block->border_top); - } - - // render bottom border - if (block->border != 0 && block->border_bottom > 0) { - render_sharp_line(window->cairo, block->border, - pos - 0.5, - (window->height * window->scale) - 1 - block->border_bottom, - block_width, - block->border_bottom); - } - - // render left border - if (block->border != 0 && block->border_left > 0) { - render_sharp_line(window->cairo, block->border, - pos - 0.5, - 1, - block->border_left, - (window->height * window->scale) - 2); - - pos += block->border_left + margin; - } - - // render text - double offset = 0; - - if (strncmp(block->align, "left", 5) == 0) { - offset = pos; - } else if (strncmp(block->align, "right", 5) == 0) { - offset = pos + width - textwidth; - } else if (strncmp(block->align, "center", 6) == 0) { - offset = pos + (width - textwidth) / 2; - } - - cairo_move_to(window->cairo, offset, margin); - cairo_set_source_u32(window->cairo, block->color); - pango_printf(window->cairo, window->font, window->scale, - block->markup, "%s", block->full_text); - - pos += width; +#include "wlr-layer-shell-unstable-v1-client-protocol.h" - // render right border - if (block->border != 0 && block->border_right > 0) { - pos += margin; - - render_sharp_line(window->cairo, block->border, - pos - 0.5, - 1, - block->border_right, - (window->height * window->scale) - 2); - - pos += block->border_right; - } - - // render separator - if (!edge && block->separator) { - if (is_focused) { - cairo_set_source_u32(window->cairo, config->colors.focused_separator); - } else { - cairo_set_source_u32(window->cairo, config->colors.separator); - } - if (config->sep_symbol) { - offset = pos + (block->separator_block_width - sep_width) / 2; - cairo_move_to(window->cairo, offset, margin); - pango_printf(window->cairo, window->font, window->scale, - false, "%s", config->sep_symbol); - } else { - cairo_set_line_width(window->cairo, 1); - cairo_move_to(window->cairo, pos + block->separator_block_width/2, - margin); - cairo_line_to(window->cairo, pos + block->separator_block_width/2, - (window->height * window->scale) - margin); - cairo_stroke(window->cairo); - } - } +static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar, + struct swaybar_output *output) { + struct swaybar_config *config = bar->config; -} - -static const char *strip_workspace_name(bool strip_num, const char *ws_name) { - bool strip = false; - int i; - - if (strip_num) { - int len = strlen(ws_name); - for (i = 0; i < len; ++i) { - if (!('0' <= ws_name[i] && ws_name[i] <= '9')) { - if (':' == ws_name[i] && i < len-1 && i > 0) { - strip = true; - ++i; - } - break; - } - } - } - - if (strip) { - return ws_name + i; - } - - return ws_name; -} - -void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) { - const char *stripped_name = strip_workspace_name(swaybar.config->strip_workspace_numbers, workspace_name); - - get_text_size(window->cairo, window->font, width, height, - window->scale, true, "%s", stripped_name); - *width += 2 * ws_horizontal_padding; - *height += 2 * ws_vertical_padding; -} - -static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) { - const char *stripped_name = strip_workspace_name(config->strip_workspace_numbers, ws->name); - - struct box_colors box_colors; - if (ws->urgent) { - box_colors = config->colors.urgent_workspace; - } else if (ws->focused) { - box_colors = config->colors.focused_workspace; - } else if (ws->visible) { - box_colors = config->colors.active_workspace; - } else { - box_colors = config->colors.inactive_workspace; - } - - int width, height; - workspace_button_size(window, stripped_name, &width, &height); - - // background - cairo_set_source_u32(window->cairo, box_colors.background); - cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); - cairo_fill(window->cairo); - - // border - cairo_set_source_u32(window->cairo, box_colors.border); - cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); - cairo_stroke(window->cairo); - - // text - cairo_set_source_u32(window->cairo, box_colors.text); - cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin); - pango_printf(window->cairo, window->font, window->scale, - true, "%s", stripped_name); - - *x += width + ws_spacing; -} - -static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) { - int width, height; - get_text_size(window->cairo, window->font, &width, &height, - window->scale, false, "%s", config->mode); - - // background - cairo_set_source_u32(window->cairo, config->colors.binding_mode.background); - cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, - height + ws_vertical_padding * 2); - cairo_fill(window->cairo); - - // border - cairo_set_source_u32(window->cairo, config->colors.binding_mode.border); - cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, - height + ws_vertical_padding * 2); - cairo_stroke(window->cairo); - - // text - cairo_set_source_u32(window->cairo, config->colors.binding_mode.text); - cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin); - pango_printf(window->cairo, window->font, window->scale, - false, "%s", config->mode); -} - -void render(struct output *output, struct config *config, struct status_line *line) { - int i; - - struct window *window = output->window; - cairo_t *cairo = window->cairo; - bool is_focused = output->focused; - - // Clear cairo_save(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); cairo_paint(cairo); cairo_restore(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - - // Background - if (is_focused) { + if (output->focused) { cairo_set_source_u32(cairo, config->colors.focused_background); } else { cairo_set_source_u32(cairo, config->colors.background); } cairo_paint(cairo); -#ifdef ENABLE_TRAY - uint32_t tray_width = tray_render(output, config); -#else - const uint32_t tray_width = window->width * window->scale; -#endif - - // Command output - if (is_focused) { - cairo_set_source_u32(cairo, config->colors.focused_statusline); + // TODO: use actual height + return 20; +} + +void render_frame(struct swaybar *bar, + struct swaybar_output *output) { + cairo_surface_t *recorder = cairo_recording_surface_create( + CAIRO_CONTENT_COLOR_ALPHA, NULL); + cairo_t *cairo = cairo_create(recorder); + uint32_t height = render_to_cairo(cairo, bar, output); + if (height != output->height) { + // Reconfigure surface + zwlr_layer_surface_v1_set_size( + output->layer_surface, 0, height); + // TODO: this could infinite loop if the compositor assigns us a + // different height than what we asked for + wl_surface_commit(output->surface); + wl_display_roundtrip(bar->display); } else { - cairo_set_source_u32(cairo, config->colors.statusline); - } - - int width, height; - - if (line->protocol == TEXT) { - get_text_size(window->cairo, window->font, &width, &height, - window->scale, config->pango_markup, "%s", line->text_line); - cairo_move_to(cairo, tray_width - margin - width, margin); - pango_printf(window->cairo, window->font, window->scale, - config->pango_markup, "%s", line->text_line); - } else if (line->protocol == I3BAR && line->block_line) { - double pos = tray_width - 0.5; - bool edge = true; - for (i = line->block_line->length - 1; i >= 0; --i) { - struct status_block *block = line->block_line->items[i]; - if (block->full_text && block->full_text[0]) { - render_block(window, config, block, &pos, edge, is_focused); - edge = false; - } - } - } - - cairo_set_line_width(cairo, 1.0); - double x = 0.5; - - // Workspaces - if (config->workspace_buttons) { - for (i = 0; i < output->workspaces->length; ++i) { - struct workspace *ws = output->workspaces->items[i]; - render_workspace_button(window, config, ws, &x); - } - } - - // binding mode indicator - if (config->mode && config->binding_mode_indicator) { - render_binding_mode_indicator(window, config, x); - } -} - -void set_window_height(struct window *window, int height) { - int text_width, text_height; - get_text_size(window->cairo, window->font, - &text_width, &text_height, window->scale, false, - "Test string for measuring purposes"); - if (height > 0) { - margin = (height - text_height) / 2; - ws_vertical_padding = margin - 1.5; - } - window->height = (text_height + margin * 2) / window->scale; + // Replay recording into shm and send it off + output->current_buffer = get_next_buffer(bar->shm, + output->buffers, output->width, output->height); + cairo_t *shm = output->current_buffer->cairo; + cairo_set_source_surface(shm, recorder, 0.0, 0.0); + cairo_paint(shm); + wl_surface_attach(output->surface, + output->current_buffer->buffer, 0, 0); + wl_surface_damage(output->surface, 0, 0, output->width, output->height); + wl_surface_commit(output->surface); + wl_display_roundtrip(bar->display); + } + cairo_surface_destroy(recorder); + cairo_destroy(cairo); } diff --git a/swaybar/status_line.c b/swaybar/status_line.c deleted file mode 100644 index 87e90caf..00000000 --- a/swaybar/status_line.c +++ /dev/null @@ -1,530 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include - -#include "swaybar/config.h" -#include "swaybar/status_line.h" -#include "log.h" -#include "util.h" - -#define I3JSON_MAXDEPTH 4 -#define I3JSON_UNKNOWN 0 -#define I3JSON_ARRAY 1 -#define I3JSON_STRING 2 - -struct { - int bufsize; - char *buffer; - char *line_start; - char *parserpos; - bool escape; - int depth; - int bar[I3JSON_MAXDEPTH+1]; -} i3json_state = { 0, NULL, NULL, NULL, false, 0, { I3JSON_UNKNOWN } }; - -static char line[1024]; -static char line_rest[1024]; - -static char event_buff[1024]; - -static void free_status_block(void *item) { - if (!item) { - return; - } - struct status_block *sb = (struct status_block*)item; - if (sb->full_text) { - free(sb->full_text); - } - if (sb->short_text) { - free(sb->short_text); - } - if (sb->align) { - free(sb->align); - } - if (sb->name) { - free(sb->name); - } - if (sb->instance) { - free(sb->instance); - } - free(sb); -} - -static void parse_json(struct bar *bar, const char *text) { - json_object *results = json_tokener_parse(text); - if (!results) { - sway_log(L_DEBUG, "Failed to parse json"); - return; - } - - if (json_object_array_length(results) < 1) { - return; - } - - if (bar->status->block_line) { - list_foreach(bar->status->block_line, free_status_block); - list_free(bar->status->block_line); - } - - bar->status->block_line = create_list(); - - int i; - for (i = 0; i < json_object_array_length(results); ++i) { - json_object *full_text, *short_text, *color, *min_width, *align, *urgent; - json_object *name, *instance, *separator, *separator_block_width; - json_object *background, *border, *border_top, *border_bottom; - json_object *border_left, *border_right, *markup; - - json_object *json = json_object_array_get_idx(results, i); - if (!json) { - continue; - } - - json_object_object_get_ex(json, "full_text", &full_text); - json_object_object_get_ex(json, "short_text", &short_text); - json_object_object_get_ex(json, "color", &color); - json_object_object_get_ex(json, "min_width", &min_width); - json_object_object_get_ex(json, "align", &align); - json_object_object_get_ex(json, "urgent", &urgent); - json_object_object_get_ex(json, "name", &name); - json_object_object_get_ex(json, "instance", &instance); - json_object_object_get_ex(json, "markup", &markup); - json_object_object_get_ex(json, "separator", &separator); - json_object_object_get_ex(json, "separator_block_width", &separator_block_width); - json_object_object_get_ex(json, "background", &background); - json_object_object_get_ex(json, "border", &border); - json_object_object_get_ex(json, "border_top", &border_top); - json_object_object_get_ex(json, "border_bottom", &border_bottom); - json_object_object_get_ex(json, "border_left", &border_left); - json_object_object_get_ex(json, "border_right", &border_right); - - struct status_block *new = calloc(1, sizeof(struct status_block)); - - if (full_text) { - new->full_text = strdup(json_object_get_string(full_text)); - } - - if (short_text) { - new->short_text = strdup(json_object_get_string(short_text)); - } - - if (color) { - new->color = parse_color(json_object_get_string(color)); - } else { - new->color = bar->config->colors.statusline; - } - - if (min_width) { - json_type type = json_object_get_type(min_width); - if (type == json_type_int) { - new->min_width = json_object_get_int(min_width); - } else if (type == json_type_string) { - /* the width will be calculated when rendering */ - new->min_width = 0; - } - } - - if (align) { - new->align = strdup(json_object_get_string(align)); - } else { - new->align = strdup("left"); - } - - if (urgent) { - new->urgent = json_object_get_int(urgent); - } - - if (name) { - new->name = strdup(json_object_get_string(name)); - } - - if (instance) { - new->instance = strdup(json_object_get_string(instance)); - } - - if (markup) { - new->markup = false; - const char *markup_str = json_object_get_string(markup); - if (strcmp(markup_str, "pango") == 0) { - new->markup = true; - } - } - - if (separator) { - new->separator = json_object_get_int(separator); - } else { - new->separator = true; // i3bar spec - } - - if (separator_block_width) { - new->separator_block_width = json_object_get_int(separator_block_width); - } else { - new->separator_block_width = 9; // i3bar spec - } - - // Airblader features - if (background) { - new->background = parse_color(json_object_get_string(background)); - } else { - new->background = 0x0; // transparent - } - - if (border) { - new->border = parse_color(json_object_get_string(border)); - } else { - new->border = 0x0; // transparent - } - - if (border_top) { - new->border_top = json_object_get_int(border_top); - } else { - new->border_top = 1; - } - - if (border_bottom) { - new->border_bottom = json_object_get_int(border_bottom); - } else { - new->border_bottom = 1; - } - - if (border_left) { - new->border_left = json_object_get_int(border_left); - } else { - new->border_left = 1; - } - - if (border_right) { - new->border_right = json_object_get_int(border_right); - } else { - new->border_right = 1; - } - - list_add(bar->status->block_line, new); - } - - json_object_put(results); -} - -// continue parsing from last parserpos -static int i3json_parse(struct bar *bar) { - char *c = i3json_state.parserpos; - int handled = 0; - while (*c) { - if (i3json_state.bar[i3json_state.depth] == I3JSON_STRING) { - if (!i3json_state.escape && *c == '"') { - --i3json_state.depth; - } - i3json_state.escape = !i3json_state.escape && *c == '\\'; - } else { - switch (*c) { - case '[': - ++i3json_state.depth; - if (i3json_state.depth > I3JSON_MAXDEPTH) { - sway_abort("JSON too deep"); - } - i3json_state.bar[i3json_state.depth] = I3JSON_ARRAY; - if (i3json_state.depth == 2) { - i3json_state.line_start = c; - } - break; - case ']': - if (i3json_state.bar[i3json_state.depth] != I3JSON_ARRAY) { - sway_abort("JSON malformed"); - } - --i3json_state.depth; - if (i3json_state.depth == 1) { - // c[1] is valid since c[0] != '\0' - char p = c[1]; - c[1] = '\0'; - parse_json(bar, i3json_state.line_start); - c[1] = p; - ++handled; - i3json_state.line_start = c+1; - } - break; - case '"': - ++i3json_state.depth; - if (i3json_state.depth > I3JSON_MAXDEPTH) { - sway_abort("JSON too deep"); - } - i3json_state.bar[i3json_state.depth] = I3JSON_STRING; - break; - } - } - ++c; - } - i3json_state.parserpos = c; - return handled; -} - -// Read line from file descriptor, only show the line tail if it is too long. -// In non-blocking mode treat "no more data" as a linebreak. -// If data after a line break has been read, return it in rest. -// If rest is non-empty, then use that as the start of the next line. -static int read_line_tail(int fd, char *buf, int nbyte, char *rest) { - if (fd < 0 || !buf || !nbyte) { - return -1; - } - int l; - char *buffer = malloc(nbyte*2+1); - char *readpos = buffer; - char *lf; - // prepend old data to new line if necessary - if (rest) { - l = strlen(rest); - if (l > nbyte) { - strcpy(buffer, rest + l - nbyte); - readpos += nbyte; - } else if (l) { - strcpy(buffer, rest); - readpos += l; - } - } - // read until a linefeed is found or no more data is available - while ((l = read(fd, readpos, nbyte)) > 0) { - readpos[l] = '\0'; - lf = strchr(readpos, '\n'); - if (lf) { - // linefeed found, replace with \0 - *lf = '\0'; - // give data from the end of the line, try to fill the buffer - if (lf-buffer > nbyte) { - strcpy(buf, lf - nbyte + 1); - } else { - strcpy(buf, buffer); - } - // we may have read data from the next line, save it to rest - if (rest) { - rest[0] = '\0'; - strcpy(rest, lf + 1); - } - free(buffer); - return strlen(buf); - } else { - // no linefeed found, slide data back. - int overflow = readpos - buffer + l - nbyte; - if (overflow > 0) { - memmove(buffer, buffer + overflow , nbyte + 1); - } - } - } - if (l < 0) { - free(buffer); - return l; - } - readpos[l]='\0'; - if (rest) { - rest[0] = '\0'; - } - if (nbyte < readpos - buffer + l - 1) { - memcpy(buf, readpos - nbyte + l + 1, nbyte); - } else { - strncpy(buf, buffer, nbyte); - } - buf[nbyte-1] = '\0'; - free(buffer); - return strlen(buf); -} - -// make sure that enough buffer space is available starting from parserpos -static void i3json_ensure_free(int min_free) { - int _step = 10240; - int r = min_free % _step; - if (r) { - min_free += _step - r; - } - if (!i3json_state.buffer) { - i3json_state.buffer = malloc(min_free); - i3json_state.bufsize = min_free; - i3json_state.parserpos = i3json_state.buffer; - } else { - int len = 0; - int pos = 0; - if (i3json_state.line_start) { - len = strlen(i3json_state.line_start); - pos = i3json_state.parserpos - i3json_state.line_start; - if (i3json_state.line_start != i3json_state.buffer) { - memmove(i3json_state.buffer, i3json_state.line_start, len+1); - } - } else { - len = strlen(i3json_state.buffer); - } - if (i3json_state.bufsize < len+min_free) { - i3json_state.bufsize += min_free; - if (i3json_state.bufsize > 1024000) { - sway_abort("Status line json too long or malformed."); - } - i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize); - if (!i3json_state.buffer) { - sway_abort("Could not allocate json buffer"); - } - } - if (i3json_state.line_start) { - i3json_state.line_start = i3json_state.buffer; - i3json_state.parserpos = i3json_state.buffer + pos; - } else { - i3json_state.parserpos = i3json_state.buffer; - } - } - if (!i3json_state.buffer) { - sway_abort("Could not allocate buffer."); - } -} - -// append data and parse it. -static int i3json_handle_data(struct bar *bar, char *data) { - int len = strlen(data); - i3json_ensure_free(len); - strcpy(i3json_state.parserpos, data); - return i3json_parse(bar); -} - -// read data from fd and parse it. -static int i3json_handle_fd(struct bar *bar) { - i3json_ensure_free(10240); - // get fresh data at the end of the buffer - int readlen = read(bar->status_read_fd, i3json_state.parserpos, 10239); - if (readlen < 0) { - return readlen; - } - i3json_state.parserpos[readlen] = '\0'; - return i3json_parse(bar); -} - -bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button) { - sway_log(L_DEBUG, "status_line_mouse_event."); - if (!bar->status->click_events) { - sway_log(L_DEBUG, "click_events are not enabled."); - return false; - } - - if (bar->status->protocol == I3BAR) { - sway_log(L_DEBUG, "Sending click event."); - - // find clicked block - struct status_block *clicked_block = NULL; - struct status_block *current_block = NULL; - int num_blocks = bar->status->block_line->length; - - if (num_blocks == 0) { - return false; - } else { - current_block = bar->status->block_line->items[0]; - if (x < current_block->x) { - return false; - } - } - - for (int i = 0; i < num_blocks; i++) { - current_block = bar->status->block_line->items[i]; - if (x < (current_block->x + current_block->width)) { - clicked_block = current_block; - break; - } - } - - if (!clicked_block || !clicked_block->name) { - return false; - } - - // event example {"name":"capture","instance":"label","button":1,"x":3431,"y":18} - - struct json_object *event_json = json_object_new_object(); - json_object_object_add(event_json, "name", json_object_new_string(clicked_block->name)); - if (clicked_block->instance) { - json_object_object_add(event_json, "instance", json_object_new_string(clicked_block->instance)); - } - json_object_object_add(event_json, "button", json_object_new_int(button)); - json_object_object_add(event_json, "x", json_object_new_int(x)); - json_object_object_add(event_json, "y", json_object_new_int(y)); - - int len = snprintf(event_buff, sizeof(event_buff), "%s\n", json_object_to_json_string(event_json)); - - json_object_put(event_json); - - if (len <= (int)sizeof(event_buff)) { // if not truncated - write(bar->status_write_fd, event_buff, len); - return true; - } - } - - return false; -} - -bool handle_status_line(struct bar *bar) { - bool dirty = false; - - switch (bar->status->protocol) { - case I3BAR: - sway_log(L_DEBUG, "Got i3bar protocol."); - if (i3json_handle_fd(bar) > 0) { - dirty = true; - } - break; - case TEXT: - sway_log(L_DEBUG, "Got text protocol."); - read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest); - dirty = true; - bar->status->text_line = line; - break; - case UNDEF: - sway_log(L_DEBUG, "Detecting protocol..."); - if (read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest) < 0) { - break; - } - dirty = true; - bar->status->text_line = line; - bar->status->protocol = TEXT; - if (line[0] == '{') { - // detect i3bar json protocol - json_object *proto = json_tokener_parse(line); - if (proto) { - - json_object *version; - if (json_object_object_get_ex(proto, "version", &version) - && json_object_get_int(version) == 1 - ) { - sway_log(L_DEBUG, "Switched to i3bar protocol."); - bar->status->protocol = I3BAR; - } - - json_object *click_events; - if (json_object_object_get_ex(proto, "click_events", &click_events) - && json_object_get_boolean(click_events)) { - - sway_log(L_DEBUG, "Enabling click events."); - bar->status->click_events = true; - - const char *events_array = "[\n"; - write(bar->status_write_fd, events_array, strlen(events_array)); - } - - i3json_handle_data(bar, line_rest); - - json_object_put(proto); - } - } - break; - } - - return dirty; -} - -struct status_line *init_status_line() { - struct status_line *line = malloc(sizeof(struct status_line)); - line->block_line = create_list(); - line->text_line = NULL; - line->protocol = UNDEF; - line->click_events = false; - - return line; -} - -void free_status_line(struct status_line *line) { - if (line->block_line) { - list_foreach(line->block_line, free_status_block); - list_free(line->block_line); - } -} diff --git a/swaybar/tray/dbus.c b/swaybar/tray/dbus.c deleted file mode 100644 index 8e719fd9..00000000 --- a/swaybar/tray/dbus.c +++ /dev/null @@ -1,197 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include -#include -#include -#include -#include -#include "swaybar/tray/dbus.h" -#include "swaybar/event_loop.h" -#include "log.h" - -DBusConnection *conn = NULL; - -static void dispatch_watch(int fd, short mask, void *data) { - sway_log(L_DEBUG, "Dispatching watch"); - DBusWatch *watch = data; - - if (!dbus_watch_get_enabled(watch)) { - return; - } - - uint32_t flags = 0; - - if (mask & POLLIN) { - flags |= DBUS_WATCH_READABLE; - } if (mask & POLLOUT) { - flags |= DBUS_WATCH_WRITABLE; - } if (mask & POLLHUP) { - flags |= DBUS_WATCH_HANGUP; - } if (mask & POLLERR) { - flags |= DBUS_WATCH_ERROR; - } - - dbus_watch_handle(watch, flags); -} - -static dbus_bool_t add_watch(DBusWatch *watch, void *_data) { - if (!dbus_watch_get_enabled(watch)) { - // Watch should not be polled - return TRUE; - } - - short mask = 0; - uint32_t flags = dbus_watch_get_flags(watch); - - if (flags & DBUS_WATCH_READABLE) { - mask |= POLLIN; - } if (flags & DBUS_WATCH_WRITABLE) { - mask |= POLLOUT; - } - - int fd = dbus_watch_get_unix_fd(watch); - - sway_log(L_DEBUG, "Adding DBus watch fd: %d", fd); - add_event(fd, mask, dispatch_watch, watch); - - return TRUE; -} - -static void remove_watch(DBusWatch *watch, void *_data) { - int fd = dbus_watch_get_unix_fd(watch); - - remove_event(fd); -} - -static void dispatch_timeout(timer_t timer, void *data) { - sway_log(L_DEBUG, "Dispatching DBus timeout"); - DBusTimeout *timeout = data; - - if (dbus_timeout_get_enabled(timeout)) { - dbus_timeout_handle(timeout); - } -} - -static dbus_bool_t add_timeout(DBusTimeout *timeout, void *_data) { - if (!dbus_timeout_get_enabled(timeout)) { - return TRUE; - } - - timer_t *timer = malloc(sizeof(timer_t)); - if (!timer) { - sway_log(L_ERROR, "Cannot allocate memory"); - return FALSE; - } - struct sigevent ev = { - .sigev_notify = SIGEV_NONE, - }; - - if (timer_create(CLOCK_MONOTONIC, &ev, timer)) { - sway_log(L_ERROR, "Could not create DBus timer"); - return FALSE; - } - - int interval = dbus_timeout_get_interval(timeout); - int interval_sec = interval / 1000; - int interval_msec = (interval_sec * 1000) - interval; - - struct timespec period = { - (time_t) interval_sec, - ((long) interval_msec) * 1000 * 1000, - }; - struct itimerspec time = { - period, - period, - }; - - timer_settime(*timer, 0, &time, NULL); - - dbus_timeout_set_data(timeout, timer, NULL); - - sway_log(L_DEBUG, "Adding DBus timeout. Interval: %ds %dms", interval_sec, interval_msec); - add_timer(*timer, dispatch_timeout, timeout); - - return TRUE; -} -static void remove_timeout(DBusTimeout *timeout, void *_data) { - timer_t *timer = (timer_t *) dbus_timeout_get_data(timeout); - sway_log(L_DEBUG, "Removing DBus timeout."); - - if (timer) { - remove_timer(*timer); - timer_delete(*timer); - free(timer); - } -} - -static bool should_dispatch = true; - -static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_status, - void *_data) { - if (new_status == DBUS_DISPATCH_DATA_REMAINS) { - should_dispatch = true; - } -} - -/* Public functions below */ - -void dispatch_dbus() { - if (!should_dispatch || !conn) { - return; - } - - DBusDispatchStatus status; - - do { - status = dbus_connection_dispatch(conn); - } while (status == DBUS_DISPATCH_DATA_REMAINS); - - if (status != DBUS_DISPATCH_COMPLETE) { - sway_log(L_ERROR, "Cannot dispatch dbus events: %d", status); - } - - should_dispatch = false; -} - -int dbus_init() { - DBusError error; - dbus_error_init(&error); - - conn = dbus_bus_get(DBUS_BUS_SESSION, &error); - if (conn == NULL) { - sway_log(L_INFO, "Compiled with dbus support, but unable to connect to dbus"); - sway_log(L_INFO, "swaybar will be unable to display tray icons."); - return -1; - } - - dbus_connection_set_exit_on_disconnect(conn, FALSE); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "Cannot get bus connection: %s\n", error.message); - conn = NULL; - return -1; - } - - sway_log(L_INFO, "Unique name: %s\n", dbus_bus_get_unique_name(conn)); - - // Will be called if dispatch status changes - dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL); - - if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch, - NULL, NULL, NULL)) { - dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); - sway_log(L_ERROR, "Failed to activate DBUS watch functions"); - return -1; - } - - if (!dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, - NULL, NULL, NULL)) { - dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); - dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL); - sway_log(L_ERROR, "Failed to activate DBUS timeout functions"); - return -1; - } - - return 0; -} diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c deleted file mode 100644 index c146bf32..00000000 --- a/swaybar/tray/icon.c +++ /dev/null @@ -1,400 +0,0 @@ -#define _XOPEN_SOURCE 700 -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "swaybar/tray/icon.h" -#include "swaybar/bar.h" -#include "swaybar/config.h" -#include "stringop.h" -#include "log.h" - -/** - * REVIEW: - * This file repeats lots of "costly" operations that are the same for every - * icon. It's possible to create a dictionary or some other structure to cache - * these, though it may complicate things somewhat. - * - * Also parsing (index.theme) is currently pretty messy, so that could be made - * much better as well. Over all, things work, but are not optimal. - */ - -/* Finds all themes that the given theme inherits */ -static list_t *find_inherits(const char *theme_dir) { - const char inherits[] = "Inherits"; - const char index_name[] = "index.theme"; - list_t *themes = create_list(); - FILE *index = NULL; - char *path = malloc(strlen(theme_dir) + sizeof(index_name)); - if (!path) { - goto fail; - } - if (!themes) { - goto fail; - } - - strcpy(path, theme_dir); - strcat(path, index_name); - - index = fopen(path, "r"); - if (!index) { - goto fail; - } - - char *buf = NULL; - size_t n = 0; - while (!feof(index) && getline(&buf, &n, index) != -1) { - if (n <= sizeof(inherits) + 1) { - continue; - } - if (strncmp(inherits, buf, sizeof(inherits) - 1) == 0) { - char *themestr = buf + sizeof(inherits); - themes = split_string(themestr, ","); - break; - } - } - free(buf); - -fail: - free(path); - if (index) { - fclose(index); - } - return themes; -} - -static bool isdir(const char *path) { - struct stat statbuf; - if (stat(path, &statbuf) != -1) { - if (S_ISDIR(statbuf.st_mode)) { - return true; - } - } - return false; - -} - -/** - * Returns the directory of a given theme if it exists. - * The returned pointer must be freed. - */ -static char *find_theme_dir(const char *theme) { - char *basedir; - char *icon_dir; - - if (!theme) { - return NULL; - } - - if (!(icon_dir = malloc(1024))) { - sway_log(L_ERROR, "Out of memory!"); - goto fail; - } - - if ((basedir = getenv("HOME"))) { - if (snprintf(icon_dir, 1024, "%s/.icons/%s", basedir, theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - // XXX perhaps just goto trying in /usr/share? This - // shouldn't happen anyway, but might with a long global - goto fail; - } - - if (isdir(icon_dir)) { - return icon_dir; - } - } - - if ((basedir = getenv("XDG_DATA_DIRS"))) { - if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - // ditto - goto fail; - } - - if (isdir(icon_dir)) { - return icon_dir; - } - } - - // Spec says use "/usr/share/pixmaps/", but I see everything in - // "/usr/share/icons/" look it both, I suppose. - if (snprintf(icon_dir, 1024, "/usr/share/pixmaps/%s", theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - goto fail; - } - if (isdir(icon_dir)) { - return icon_dir; - } - - if (snprintf(icon_dir, 1024, "/usr/share/icons/%s", theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - goto fail; - } - if (isdir(icon_dir)) { - return icon_dir; - } - -fail: - free(icon_dir); - sway_log(L_ERROR, "Could not find dir for theme: %s", theme); - return NULL; -} - -/** - * Returns all theme dirs needed to be looked in for an icon. - * Does not check for duplicates - */ -static list_t *find_all_theme_dirs(const char *theme) { - list_t *dirs = create_list(); - if (!dirs) { - return NULL; - } - char *dir = find_theme_dir(theme); - if (dir) { - list_add(dirs, dir); - list_t *inherits = find_inherits(dir); - list_cat(dirs, inherits); - list_free(inherits); - } - dir = find_theme_dir("hicolor"); - if (dir) { - list_add(dirs, dir); - } - - return dirs; -} - -struct subdir { - int size; - char name[]; -}; - -static int subdir_str_cmp(const void *_subdir, const void *_str) { - const struct subdir *subdir = _subdir; - const char *str = _str; - return strcmp(subdir->name, str); -} -/** - * Helper to find_subdirs. Acts similar to `split_string(subdirs, ",")` but - * generates a list of struct subdirs - */ -static list_t *split_subdirs(char *subdir_str) { - list_t *subdir_list = create_list(); - char *copy = strdup(subdir_str); - if (!subdir_list || !copy) { - list_free(subdir_list); - free(copy); - return NULL; - } - - char *token; - token = strtok(copy, ","); - while(token) { - int len = strlen(token) + 1; - struct subdir *subdir = - malloc(sizeof(struct subdir) + sizeof(char [len])); - if (!subdir) { - // Return what we have - return subdir_list; - } - subdir->size = 0; - strcpy(subdir->name, token); - - list_add(subdir_list, subdir); - - token = strtok(NULL, ","); - } - free(copy); - - return subdir_list; -} -/** - * Returns a list of all subdirectories of a theme. - * Take note: the subdir names are all relative to `theme_dir` and must be - * combined with it to form a valid directory. - * - * Each member of the list is of type (struct subdir *) this struct contains - * the name of the subdir, along with size information. These must be freed - * bye the caller. - * - * This currently ignores min and max sizes of icons. - */ -static list_t* find_theme_subdirs(const char *theme_dir) { - const char index_name[] = "/index.theme"; - list_t *dirs = NULL; - char *path = malloc(strlen(theme_dir) + sizeof(index_name)); - FILE *index = NULL; - if (!path) { - sway_log(L_ERROR, "Failed to allocate memory"); - goto fail; - } - - strcpy(path, theme_dir); - strcat(path, index_name); - - index = fopen(path, "r"); - if (!index) { - sway_log(L_ERROR, "Could not open file: %s", path); - goto fail; - } - - char *buf = NULL; - size_t n = 0; - const char directories[] = "Directories"; - while (!feof(index) && getline(&buf, &n, index) != -1) { - if (n <= sizeof(directories) + 1) { - continue; - } - if (strncmp(directories, buf, sizeof(directories) - 1) == 0) { - char *dirstr = buf + sizeof(directories); - dirs = split_subdirs(dirstr); - break; - } - } - // Now, find the size of each dir - struct subdir *current_subdir = NULL; - const char size[] = "Size"; - while (!feof(index) && getline(&buf, &n, index) != -1) { - if (buf[0] == '[') { - int len = strlen(buf); - if (buf[len-1] == '\n') { - len--; - } - // replace ']' - buf[len-1] = '\0'; - - int index; - if ((index = list_seq_find(dirs, subdir_str_cmp, buf+1)) != -1) { - current_subdir = (dirs->items[index]); - } - } - - if (strncmp(size, buf, sizeof(size) - 1) == 0) { - if (current_subdir) { - current_subdir->size = atoi(buf + sizeof(size)); - } - } - } - free(buf); -fail: - free(path); - if (index) { - fclose(index); - } - return dirs; -} - -/* Returns the file of an icon given its name and size */ -static char *find_icon_file(const char *name, int size) { - int namelen = strlen(name); - list_t *dirs = find_all_theme_dirs(swaybar.config->icon_theme); - if (!dirs) { - return NULL; - } - int min_size_diff = INT_MAX; - char *current_file = NULL; - - for (int i = 0; i < dirs->length; ++i) { - char *dir = dirs->items[i]; - list_t *subdirs = find_theme_subdirs(dir); - - if (!subdirs) { - continue; - } - - for (int i = 0; i < subdirs->length; ++i) { - struct subdir *subdir = subdirs->items[i]; - - // Only use an unsized if we don't already have a - // canidate this should probably change to allow svgs - if (!subdir->size && current_file) { - continue; - } - - int size_diff = abs(size - subdir->size); - - if (size_diff >= min_size_diff) { - continue; - } - - char *path = malloc(strlen(subdir->name) + strlen(dir) + 2); - - strcpy(path, dir); - path[strlen(dir)] = '/'; - strcpy(path + strlen(dir) + 1, subdir->name); - - DIR *icons = opendir(path); - if (!icons) { - free(path); - continue; - } - - struct dirent *direntry; - while ((direntry = readdir(icons)) != NULL) { - int len = strlen(direntry->d_name); - if (len <= namelen + 2) { //must have some ext - continue; - } - if (strncmp(direntry->d_name, name, namelen) == 0) { - char *ext = direntry->d_name + namelen + 1; -#ifdef WITH_GDK_PIXBUF - if (strcmp(ext, "png") == 0 || - strcmp(ext, "xpm") == 0 || - strcmp(ext, "svg") == 0) { -#else - if (strcmp(ext, "png") == 0) { -#endif - free(current_file); - char *icon_path = malloc(strlen(path) + len + 2); - - strcpy(icon_path, path); - icon_path[strlen(path)] = '/'; - strcpy(icon_path + strlen(path) + 1, direntry->d_name); - current_file = icon_path; - min_size_diff = size_diff; - } - } - } - free(path); - closedir(icons); - } - free_flat_list(subdirs); - } - free_flat_list(dirs); - - return current_file; -} - -cairo_surface_t *find_icon(const char *name, int size) { - char *image_path = find_icon_file(name, size); - if (image_path == NULL) { - return NULL; - } - - cairo_surface_t *image = NULL; -#ifdef WITH_GDK_PIXBUF - GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err); - if (!pixbuf) { - sway_log(L_ERROR, "Failed to load icon image: %s", err->message); - } - image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); - g_object_unref(pixbuf); -#else - // TODO make svg work? cairo supports it. maybe remove gdk alltogether - image = cairo_image_surface_create_from_png(image_path); -#endif //WITH_GDK_PIXBUF - if (!image) { - sway_log(L_ERROR, "Could not read icon image"); - return NULL; - } - - free(image_path); - return image; -} diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c deleted file mode 100644 index c9d00657..00000000 --- a/swaybar/tray/sni.c +++ /dev/null @@ -1,481 +0,0 @@ -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include -#include -#include -#include -#include -#include "swaybar/tray/dbus.h" -#include "swaybar/tray/sni.h" -#include "swaybar/tray/icon.h" -#include "swaybar/bar.h" -#include "client/cairo.h" -#include "log.h" - -// Not sure what this is but cairo needs it. -static const cairo_user_data_key_t cairo_user_data_key; - -struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item, - int height) { - struct sni_icon_ref *sni_ref = malloc(sizeof(struct sni_icon_ref)); - if (!sni_ref) { - return NULL; - } - sni_ref->icon = cairo_image_surface_scale(item->image, height, height); - sni_ref->ref = item; - - return sni_ref; -} - -void sni_icon_ref_free(struct sni_icon_ref *sni_ref) { - if (!sni_ref) { - return; - } - cairo_surface_destroy(sni_ref->icon); - free(sni_ref); -} - -/* Gets the pixmap of an icon */ -static void reply_icon(DBusPendingCall *pending, void *_data) { - struct StatusNotifierItem *item = _data; - - DBusMessage *reply = dbus_pending_call_steal_reply(pending); - - if (!reply) { - sway_log(L_ERROR, "Did not get reply"); - goto bail; - } - - int message_type = dbus_message_get_type(reply); - - if (message_type == DBUS_MESSAGE_TYPE_ERROR) { - char *msg; - - dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &msg, - DBUS_TYPE_INVALID); - - sway_log(L_ERROR, "Message is error: %s", msg); - goto bail; - } - - DBusMessageIter iter; - DBusMessageIter variant; /* v[a(iiay)] */ - DBusMessageIter array; /* a(iiay) */ - DBusMessageIter d_struct; /* (iiay) */ - DBusMessageIter icon; /* ay */ - - dbus_message_iter_init(reply, &iter); - - // Each if here checks the types above before recursing - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"v\", is \"%s\"", - dbus_message_iter_get_signature(&iter)); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - - if (strcmp("a(iiay)", dbus_message_iter_get_signature(&variant)) != 0) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"a(iiay)\", is \"%s\"", - dbus_message_iter_get_signature(&variant)); - goto bail; - } - - if (dbus_message_iter_get_element_count(&variant) == 0) { - // Can't recurse if there are no items - sway_log(L_INFO, "Item has no icon"); - goto bail; - } - dbus_message_iter_recurse(&variant, &array); - - dbus_message_iter_recurse(&array, &d_struct); - - int width; - dbus_message_iter_get_basic(&d_struct, &width); - dbus_message_iter_next(&d_struct); - - int height; - dbus_message_iter_get_basic(&d_struct, &height); - dbus_message_iter_next(&d_struct); - - int len = dbus_message_iter_get_element_count(&d_struct); - - if (!len) { - sway_log(L_ERROR, "No icon data"); - goto bail; - } - - // Also implies len % 4 == 0, useful below - if (len != width * height * 4) { - sway_log(L_ERROR, "Incorrect array size passed"); - goto bail; - } - - dbus_message_iter_recurse(&d_struct, &icon); - - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - // FIXME support a variable stride - // (works on my machine though for all tested widths) - if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) { - goto bail; - } - - // Data is by reference, no need to free - uint8_t *message_data; - dbus_message_iter_get_fixed_array(&icon, &message_data, &len); - - uint8_t *image_data = malloc(stride * height); - if (!image_data) { - sway_log(L_ERROR, "Could not allocate memory for icon"); - goto bail; - } - - // Transform from network byte order to host byte order - // Assumptions are safe because the equality above - uint32_t *network = (uint32_t *) message_data; - uint32_t *host = (uint32_t *)image_data; - for (int i = 0; i < width * height; ++i) { - host[i] = ntohl(network[i]); - } - - cairo_surface_t *image = cairo_image_surface_create_for_data( - image_data, CAIRO_FORMAT_ARGB32, - width, height, stride); - - if (image) { - if (item->image) { - cairo_surface_destroy(item->image); - } - item->image = image; - // Free the image data on surface destruction - cairo_surface_set_user_data(image, - &cairo_user_data_key, - image_data, - free); - item->dirty = true; - dirty = true; - - dbus_message_unref(reply); - dbus_pending_call_unref(pending); - return; - } else { - sway_log(L_ERROR, "Could not create image surface"); - free(image_data); - } - -bail: - if (reply) { - dbus_message_unref(reply); - } - dbus_pending_call_unref(pending); - sway_log(L_ERROR, "Could not get icon from item"); - return; -} -static void send_icon_msg(struct StatusNotifierItem *item) { - DBusPendingCall *pending; - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - "org.freedesktop.DBus.Properties", - "Get"); - const char *iface; - if (item->kde_special_snowflake) { - iface = "org.kde.StatusNotifierItem"; - } else { - iface = "org.freedesktop.StatusNotifierItem"; - } - const char *prop = "IconPixmap"; - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); - - bool status = - dbus_connection_send_with_reply(conn, message, &pending, -1); - - dbus_message_unref(message); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get item icon"); - return; - } - - dbus_pending_call_set_notify(pending, reply_icon, item, NULL); -} - -/* Get an icon by its name */ -static void reply_icon_name(DBusPendingCall *pending, void *_data) { - struct StatusNotifierItem *item = _data; - - DBusMessage *reply = dbus_pending_call_steal_reply(pending); - - if (!reply) { - sway_log(L_INFO, "Got no icon name reply from item"); - goto bail; - } - - int message_type = dbus_message_get_type(reply); - - if (message_type == DBUS_MESSAGE_TYPE_ERROR) { - char *msg; - - dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &msg, - DBUS_TYPE_INVALID); - - sway_log(L_INFO, "Could not get icon name: %s", msg); - goto bail; - } - - DBusMessageIter iter; /* v[s] */ - DBusMessageIter variant; /* s */ - - dbus_message_iter_init(reply, &iter); - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"v\", is \"%s\"", - dbus_message_iter_get_signature(&iter)); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - - - if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"s\", is \"%s\"", - dbus_message_iter_get_signature(&iter)); - goto bail; - } - - char *icon_name; - dbus_message_iter_get_basic(&variant, &icon_name); - - cairo_surface_t *image = find_icon(icon_name, 256); - - if (image) { - sway_log(L_DEBUG, "Icon for %s found with size %d", icon_name, - cairo_image_surface_get_width(image)); - if (item->image) { - cairo_surface_destroy(item->image); - } - item->image = image; - item->dirty = true; - dirty = true; - - dbus_message_unref(reply); - dbus_pending_call_unref(pending); - return; - } - -bail: - if (reply) { - dbus_message_unref(reply); - } - dbus_pending_call_unref(pending); - // Now try the pixmap - send_icon_msg(item); - return; -} -static void send_icon_name_msg(struct StatusNotifierItem *item) { - DBusPendingCall *pending; - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - "org.freedesktop.DBus.Properties", - "Get"); - const char *iface; - if (item->kde_special_snowflake) { - iface = "org.kde.StatusNotifierItem"; - } else { - iface = "org.freedesktop.StatusNotifierItem"; - } - const char *prop = "IconName"; - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); - - bool status = - dbus_connection_send_with_reply(conn, message, &pending, -1); - - dbus_message_unref(message); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get item icon name"); - return; - } - - dbus_pending_call_set_notify(pending, reply_icon_name, item, NULL); -} - -void get_icon(struct StatusNotifierItem *item) { - send_icon_name_msg(item); -} - -void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) { - const char *iface = - (item->kde_special_snowflake ? "org.kde.StatusNotifierItem" - : "org.freedesktop.StatusNotifierItem"); - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - iface, - "Activate"); - - dbus_message_append_args(message, - DBUS_TYPE_INT32, &x, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} - -void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) { - const char *iface = - (item->kde_special_snowflake ? "org.kde.StatusNotifierItem" - : "org.freedesktop.StatusNotifierItem"); - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - iface, - "ContextMenu"); - - dbus_message_append_args(message, - DBUS_TYPE_INT32, &x, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} -void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) { - const char *iface = - (item->kde_special_snowflake ? "org.kde.StatusNotifierItem" - : "org.freedesktop.StatusNotifierItem"); - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - iface, - "SecondaryActivate"); - - dbus_message_append_args(message, - DBUS_TYPE_INT32, &x, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} - -static void get_unique_name(struct StatusNotifierItem *item) { - // I think that we're fine being sync here becaues the message is - // directly to the message bus. Could be async though. - DBusMessage *message = dbus_message_new_method_call( - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetNameOwner"); - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &item->name, - DBUS_TYPE_INVALID); - - DBusMessage *reply = dbus_connection_send_with_reply_and_block( - conn, message, -1, NULL); - - dbus_message_unref(message); - - if (!reply) { - sway_log(L_ERROR, "Could not get unique name for item: %s", - item->name); - return; - } - - char *unique_name; - if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &unique_name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing method args"); - } else { - if (item->unique_name) { - free(item->unique_name); - } - item->unique_name = strdup(unique_name); - } - - dbus_message_unref(reply); -} - -struct StatusNotifierItem *sni_create(const char *name) { - // Make sure `name` is well formed - if (!dbus_validate_bus_name(name, NULL)) { - sway_log(L_INFO, "Name (%s) is not a bus name. We cannot create an item.", name); - return NULL; - } - - struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem)); - item->name = strdup(name); - item->unique_name = NULL; - item->image = NULL; - item->dirty = false; - - // If it doesn't use this name then assume that it uses the KDE spec - // This is because xembed-sni-proxy uses neither "org.freedesktop" nor - // "org.kde" and just gives us the items "unique name" - // - // We could use this to our advantage and fill out the "unique name" - // field with the given name if it is neither freedesktop or kde, but - // that's makes us rely on KDE hackyness which is bad practice - const char freedesktop_name[] = "org.freedesktop"; - if (strncmp(name, freedesktop_name, sizeof(freedesktop_name) - 1) != 0) { - item->kde_special_snowflake = true; - } else { - item->kde_special_snowflake = false; - } - - get_icon(item); - - get_unique_name(item); - - return item; -} -/* Return 0 if `item` has a name of `str` */ -int sni_str_cmp(const void *_item, const void *_str) { - const struct StatusNotifierItem *item = _item; - const char *str = _str; - - return strcmp(item->name, str); -} -/* Returns 0 if `item` has a unique name of `str` */ -int sni_uniq_cmp(const void *_item, const void *_str) { - const struct StatusNotifierItem *item = _item; - const char *str = _str; - - if (!item->unique_name) { - return false; - } - return strcmp(item->unique_name, str); -} -void sni_free(struct StatusNotifierItem *item) { - if (!item) { - return; - } - free(item->name); - if (item->unique_name) { - free(item->unique_name); - } - if (item->image) { - cairo_surface_destroy(item->image); - } - free(item); -} diff --git a/swaybar/tray/sni_watcher.c b/swaybar/tray/sni_watcher.c deleted file mode 100644 index 86453e70..00000000 --- a/swaybar/tray/sni_watcher.c +++ /dev/null @@ -1,497 +0,0 @@ -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include -#include -#include -#include "swaybar/tray/dbus.h" -#include "list.h" -#include "log.h" - -static list_t *items = NULL; -static list_t *hosts = NULL; - -/** - * Describes the function of the StatusNotifierWatcher - * See https://freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/ - * - * We also implement KDE's special snowflake protocol, it's like this but with - * all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect. - */ -static const char *interface_xml = - "" - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - -static void host_registered_signal(DBusConnection *connection) { - // Send one signal for each protocol - DBusMessage *signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierHostRegistered"); - - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - - signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.kde.StatusNotifierWatcher", - "StatusNotifierHostRegistered"); - - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); -} -static void item_registered_signal(DBusConnection *connection, const char *name) { - DBusMessage *signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemRegistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.kde.StatusNotifierWatcher", - "StatusNotifierItemRegistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); -} -static void item_unregistered_signal(DBusConnection *connection, const char *name) { - DBusMessage *signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemUnregistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.kde.StatusNotifierWatcher", - "StatusNotifierItemUnregistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); -} - -static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) { - DBusMessage *reply; - - reply = dbus_message_new_method_return(request); - dbus_message_append_args(reply, - DBUS_TYPE_STRING, &interface_xml, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); -} - -static void register_item(DBusConnection *connection, DBusMessage *message) { - DBusError error; - char *name; - - dbus_error_init(&error); - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing method args: %s\n", error.message); - } - - sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name); - - // Don't add duplicate or not real item - if (!dbus_validate_bus_name(name, NULL)) { - sway_log(L_INFO, "This item is not valid, we cannot keep track of it."); - return; - } - - if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) { - return; - } - if (!dbus_bus_name_has_owner(connection, name, &error)) { - return; - } - - list_add(items, strdup(name)); - item_registered_signal(connection, name); - - // It's silly, but xembedsniproxy wants a reply for this function - DBusMessage *reply = dbus_message_new_method_return(message); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); -} - -static void register_host(DBusConnection *connection, DBusMessage *message) { - DBusError error; - char *name; - - dbus_error_init(&error); - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing method args: %s\n", error.message); - } - - sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name); - - // Don't add duplicate or not real host - if (!dbus_validate_bus_name(name, NULL)) { - sway_log(L_INFO, "This item is not valid, we cannot keep track of it."); - return; - } - - - if (list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name) != -1) { - return; - } - if (!dbus_bus_name_has_owner(connection, name, &error)) { - return; - } - - list_add(hosts, strdup(name)); - host_registered_signal(connection); -} - -static void get_property(DBusConnection *connection, DBusMessage *message) { - DBusError error; - char *interface; - char *property; - - dbus_error_init(&error); - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message); - return; - } - - if (strcmp(property, "RegisteredStatusNotifierItems") == 0) { - sway_log(L_INFO, "Replying with items\n"); - DBusMessage *reply; - reply = dbus_message_new_method_return(message); - DBusMessageIter iter; - DBusMessageIter sub; - DBusMessageIter subsub; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "as", &sub); - dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, - "s", &subsub); - - for (int i = 0; i < items->length; ++i) { - dbus_message_iter_append_basic(&subsub, - DBUS_TYPE_STRING, &items->items[i]); - } - - dbus_message_iter_close_container(&sub, &subsub); - dbus_message_iter_close_container(&iter, &sub); - - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } else if (strcmp(property, "IsStatusNotifierHostRegistered") == 0) { - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter sub; - int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1; - - reply = dbus_message_new_method_return(message); - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "b", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_BOOLEAN, ®istered); - - dbus_message_iter_close_container(&iter, &sub); - - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } else if (strcmp(property, "ProtocolVersion") == 0) { - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter sub; - const int version = 0; - - reply = dbus_message_new_method_return(message); - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "i", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_INT32, &version); - - dbus_message_iter_close_container(&iter, &sub); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } -} - -static void set_property(DBusConnection *connection, DBusMessage *message) { - // All properties are read only and we don't allow new properties - return; -} - -static void get_all(DBusConnection *connection, DBusMessage *message) { - DBusMessage *reply; - reply = dbus_message_new_method_return(message); - DBusMessageIter iter; /* a{v} */ - DBusMessageIter arr; - DBusMessageIter dict; - DBusMessageIter sub; - DBusMessageIter subsub; - int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1; - const int version = 0; - const char *prop; - - // Could clean this up with a function for each prop - dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - "{sv}", &arr); - - prop = "RegisteredStatusNotifierItems"; - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, - NULL, &dict); - dbus_message_iter_append_basic(&dict, - DBUS_TYPE_STRING, &prop); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, - "as", &sub); - dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, - "s", &subsub); - for (int i = 0; i < items->length; ++i) { - dbus_message_iter_append_basic(&subsub, - DBUS_TYPE_STRING, &items->items[i]); - } - dbus_message_iter_close_container(&sub, &subsub); - dbus_message_iter_close_container(&dict, &sub); - dbus_message_iter_close_container(&arr, &dict); - - prop = "IsStatusNotifierHostRegistered"; - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, - NULL, &dict); - dbus_message_iter_append_basic(&dict, - DBUS_TYPE_STRING, &prop); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, - "b", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_BOOLEAN, ®istered); - dbus_message_iter_close_container(&dict, &sub); - dbus_message_iter_close_container(&arr, &dict); - - prop = "ProtocolVersion"; - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, - NULL, &dict); - dbus_message_iter_append_basic(&dict, - DBUS_TYPE_STRING, &prop); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, - "i", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_INT32, &version); - dbus_message_iter_close_container(&dict, &sub); - dbus_message_iter_close_container(&arr, &dict); - - dbus_message_iter_close_container(&iter, &arr); - - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); -} - -static DBusHandlerResult message_handler(DBusConnection *connection, - DBusMessage *message, void *data) { - const char *interface_name = dbus_message_get_interface(message); - const char *member_name = dbus_message_get_member(message); - - // In order of the xml above - if (strcmp(interface_name, "org.freedesktop.DBus.Introspectable") == 0 && - strcmp(member_name, "Introspect") == 0) { - // We don't have an introspect for KDE - respond_to_introspect(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(interface_name, "org.freedesktop.DBus.Properties") == 0) { - if (strcmp(member_name, "Get") == 0) { - get_property(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(member_name, "Set") == 0) { - set_property(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(member_name, "GetAll") == 0) { - get_all(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } else if (strcmp(interface_name, "org.freedesktop.StatusNotifierWatcher") == 0 || - strcmp(interface_name, "org.kde.StatusNotifierWatcher") == 0) { - if (strcmp(member_name, "RegisterStatusNotifierItem") == 0) { - register_item(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(member_name, "RegisterStatusNotifierHost") == 0) { - register_host(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static DBusHandlerResult signal_handler(DBusConnection *connection, - DBusMessage *message, void *_data) { - if (dbus_message_is_signal(message, "org.freedesktop.DBus", "NameOwnerChanged")) { - // Only eat the message if it is name that we are watching - const char *name; - const char *old_owner; - const char *new_owner; - int index; - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old_owner, - DBUS_TYPE_STRING, &new_owner, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error getting LostName args"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - if (strcmp(new_owner, "") != 0) { - // Name is not lost - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - if ((index = list_seq_find(items, (int (*)(const void *, const void *))strcmp, name)) != -1) { - sway_log(L_INFO, "Status Notifier Item lost %s", name); - free(items->items[index]); - list_del(items, index); - item_unregistered_signal(connection, name); - - return DBUS_HANDLER_RESULT_HANDLED; - } - if ((index = list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name)) != -1) { - sway_log(L_INFO, "Status Notifier Host lost %s", name); - free(hosts->items[index]); - list_del(hosts, index); - - return DBUS_HANDLER_RESULT_HANDLED; - } - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static const DBusObjectPathVTable vtable = { - .message_function = message_handler, - .unregister_function = NULL, -}; - -int init_sni_watcher() { - DBusError error; - dbus_error_init(&error); - if (!conn) { - sway_log(L_ERROR, "Connection is null, cannot initiate StatusNotifierWatcher"); - return -1; - } - - items = create_list(); - hosts = create_list(); - - int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher", - DBUS_NAME_FLAG_REPLACE_EXISTING, - &error); - if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - sway_log(L_DEBUG, "Got watcher name"); - } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) { - sway_log(L_INFO, "Could not get watcher name, it may start later"); - } - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message); - return -1; - } - - status = dbus_bus_request_name(conn, "org.kde.StatusNotifierWatcher", - DBUS_NAME_FLAG_REPLACE_EXISTING, - &error); - if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - sway_log(L_DEBUG, "Got kde watcher name"); - } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) { - sway_log(L_INFO, "Could not get kde watcher name, it may start later"); - } - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message); - return -1; - } - - dbus_connection_try_register_object_path(conn, - "/StatusNotifierWatcher", - &vtable, NULL, &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err: %s\n", error.message); - return -1; - } - - dbus_bus_add_match(conn, - "type='signal',\ - sender='org.freedesktop.DBus',\ - interface='org.freedesktop.DBus',\ - member='NameOwnerChanged'", - &error); - - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "DBus error getting match args: %s", error.message); - } - - dbus_connection_add_filter(conn, signal_handler, NULL, NULL); - return 0; -} diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c deleted file mode 100644 index 91c3af06..00000000 --- a/swaybar/tray/tray.c +++ /dev/null @@ -1,398 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include -#include -#include "swaybar/bar.h" -#include "swaybar/tray/tray.h" -#include "swaybar/tray/dbus.h" -#include "swaybar/tray/sni.h" -#include "swaybar/tray/sni_watcher.h" -#include "swaybar/bar.h" -#include "swaybar/config.h" -#include "list.h" -#include "log.h" - -struct tray *tray; - -static void register_host(char *name) { - DBusMessage *message; - - message = dbus_message_new_method_call( - "org.freedesktop.StatusNotifierWatcher", - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "RegisterStatusNotifierHost"); - if (!message) { - sway_log(L_ERROR, "Cannot allocate dbus method call"); - return; - } - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} - -static void get_items_reply(DBusPendingCall *pending, void *_data) { - DBusMessage *reply = dbus_pending_call_steal_reply(pending); - - if (!reply) { - sway_log(L_ERROR, "Got no items reply from sni watcher"); - goto bail; - } - - int message_type = dbus_message_get_type(reply); - - if (message_type == DBUS_MESSAGE_TYPE_ERROR) { - char *msg; - - dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &msg, - DBUS_TYPE_INVALID); - - sway_log(L_ERROR, "Message is error: %s", msg); - goto bail; - } - - DBusMessageIter iter; - DBusMessageIter variant; - DBusMessageIter array; - - dbus_message_iter_init(reply, &iter); - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - sway_log(L_ERROR, "Replyed with wrong type, not v(as)"); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&variant) != DBUS_TYPE_STRING) { - sway_log(L_ERROR, "Replyed with wrong type, not v(as)"); - goto bail; - } - - // Clear list - list_foreach(tray->items, (void (*)(void *))sni_free); - list_free(tray->items); - tray->items = create_list(); - - // O(n) function, could be faster dynamically reading values - int len = dbus_message_iter_get_element_count(&variant); - - dbus_message_iter_recurse(&variant, &array); - for (int i = 0; i < len; i++) { - const char *name; - dbus_message_iter_get_basic(&array, &name); - - struct StatusNotifierItem *item = sni_create(name); - - if (item) { - sway_log(L_DEBUG, "Item registered with host: %s", name); - list_add(tray->items, item); - dirty = true; - } - } - -bail: - dbus_message_unref(reply); - dbus_pending_call_unref(pending); - return; -} -static void get_items() { - DBusPendingCall *pending; - DBusMessage *message = dbus_message_new_method_call( - "org.freedesktop.StatusNotifierWatcher", - "/StatusNotifierWatcher", - "org.freedesktop.DBus.Properties", - "Get"); - - const char *iface = "org.freedesktop.StatusNotifierWatcher"; - const char *prop = "RegisteredStatusNotifierItems"; - dbus_message_append_args(message, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); - - bool status = - dbus_connection_send_with_reply(conn, message, &pending, -1); - dbus_message_unref(message); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get items"); - return; - } - - dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL); -} - -static DBusHandlerResult signal_handler(DBusConnection *connection, - DBusMessage *message, void *_data) { - if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemRegistered")) { - const char *name; - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error getting StatusNotifierItemRegistered args"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - if (list_seq_find(tray->items, sni_str_cmp, name) == -1) { - struct StatusNotifierItem *item = sni_create(name); - - if (item) { - list_add(tray->items, item); - dirty = true; - } - } - - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemUnregistered")) { - const char *name; - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error getting StatusNotifierItemUnregistered args"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - int index; - if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) { - sni_free(tray->items->items[index]); - list_del(tray->items, index); - dirty = true; - } else { - // If it's not in our list, then our list is incorrect. - // Fetch all items again - sway_log(L_INFO, "Host item list incorrect, refreshing"); - get_items(); - } - - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierItem", - "NewIcon") || dbus_message_is_signal(message, - "org.kde.StatusNotifierItem", "NewIcon")) { - const char *name; - int index; - struct StatusNotifierItem *item; - - name = dbus_message_get_sender(message); - if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) { - item = tray->items->items[index]; - sway_log(L_INFO, "NewIcon signal from item %s", item->name); - get_icon(item); - } - - return DBUS_HANDLER_RESULT_HANDLED; - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static int init_host() { - tray = (struct tray *)malloc(sizeof(tray)); - - tray->items = create_list(); - - DBusError error; - dbus_error_init(&error); - char *name = NULL; - if (!conn) { - sway_log(L_ERROR, "Connection is null, cannot init SNI host"); - goto err; - } - name = calloc(sizeof(char), 256); - - if (!name) { - sway_log(L_ERROR, "Cannot allocate name"); - goto err; - } - - pid_t pid = getpid(); - if (snprintf(name, 256, "org.freedesktop.StatusNotifierHost-%d", pid) - >= 256) { - sway_log(L_ERROR, "Cannot get host name because string is too short." - "This should not happen"); - goto err; - } - - // We want to be the sole owner of this name - if (dbus_bus_request_name(conn, name, DBUS_NAME_FLAG_DO_NOT_QUEUE, - &error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - sway_log(L_ERROR, "Cannot get host name and start the tray"); - goto err; - } - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "Dbus err getting host name: %s\n", error.message); - goto err; - } - sway_log(L_DEBUG, "Got host name"); - - register_host(name); - - get_items(); - - // Perhaps use addmatch helper functions like wlc does? - dbus_bus_add_match(conn, - "type='signal',\ - sender='org.freedesktop.StatusNotifierWatcher',\ - member='StatusNotifierItemRegistered'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err: %s", error.message); - goto err; - } - dbus_bus_add_match(conn, - "type='signal',\ - sender='org.freedesktop.StatusNotifierWatcher',\ - member='StatusNotifierItemUnregistered'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err: %s", error.message); - return -1; - } - - // SNI matches - dbus_bus_add_match(conn, - "type='signal',\ - interface='org.freedesktop.StatusNotifierItem',\ - member='NewIcon'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err %s", error.message); - goto err; - } - dbus_bus_add_match(conn, - "type='signal',\ - interface='org.kde.StatusNotifierItem',\ - member='NewIcon'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err %s", error.message); - goto err; - } - - dbus_connection_add_filter(conn, signal_handler, NULL, NULL); - - free(name); - return 0; - -err: - // TODO better handle errors - free(name); - return -1; -} - -void tray_mouse_event(struct output *output, int x, int y, - uint32_t button, uint32_t state) { - - struct window *window = output->window; - uint32_t tray_padding = swaybar.config->tray_padding; - int tray_width = window->width * window->scale; - - for (int i = 0; i < output->items->length; ++i) { - struct sni_icon_ref *item = - output->items->items[i]; - int icon_width = cairo_image_surface_get_width(item->icon); - - tray_width -= tray_padding; - if (x <= tray_width && x >= tray_width - icon_width) { - if (button == swaybar.config->activate_button) { - sni_activate(item->ref, x, y); - } else if (button == swaybar.config->context_button) { - sni_context_menu(item->ref, x, y); - } else if (button == swaybar.config->secondary_button) { - sni_secondary(item->ref, x, y); - } - break; - } - tray_width -= icon_width; - } -} - -uint32_t tray_render(struct output *output, struct config *config) { - struct window *window = output->window; - cairo_t *cairo = window->cairo; - - // Tray icons - uint32_t tray_padding = config->tray_padding; - uint32_t tray_width = window->width * window->scale; - const int item_size = (window->height * window->scale) - (2 * tray_padding); - - if (item_size < 0) { - // Can't render items if the padding is too large - return tray_width; - } - - if (config->tray_output && strcmp(config->tray_output, output->name) != 0) { - return tray_width; - } - - for (int i = 0; i < tray->items->length; ++i) { - struct StatusNotifierItem *item = - tray->items->items[i]; - if (!item->image) { - continue; - } - - struct sni_icon_ref *render_item = NULL; - int j; - for (j = i; j < output->items->length; ++j) { - struct sni_icon_ref *ref = - output->items->items[j]; - if (ref->ref == item) { - render_item = ref; - break; - } else { - sni_icon_ref_free(ref); - list_del(output->items, j); - } - } - - if (!render_item) { - render_item = sni_icon_ref_create(item, item_size); - list_add(output->items, render_item); - } else if (item->dirty) { - // item needs re-render - sni_icon_ref_free(render_item); - output->items->items[j] = render_item = - sni_icon_ref_create(item, item_size); - } - - tray_width -= tray_padding; - tray_width -= item_size; - - cairo_operator_t op = cairo_get_operator(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); - cairo_set_source_surface(cairo, render_item->icon, tray_width, tray_padding); - cairo_rectangle(cairo, tray_width, tray_padding, item_size, item_size); - cairo_fill(cairo); - cairo_set_operator(cairo, op); - - item->dirty = false; - } - - - if (tray_width != window->width * window->scale) { - tray_width -= tray_padding; - } - - return tray_width; -} - -void init_tray(struct bar *bar) { - if (!bar->config->tray_output || strcmp(bar->config->tray_output, "none") != 0) { - /* Connect to the D-Bus */ - dbus_init(); - - /* Start the SNI watcher */ - init_sni_watcher(); - - /* Start the SNI host */ - init_host(); - } -} -- cgit v1.2.3 From 8d1425bde9e7f17a5a9e6bce73dffcf296dad6a1 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Fri, 30 Mar 2018 21:38:28 -0400 Subject: Initialize seat pointer in swaybar --- include/swaybar/bar.h | 10 +++++ meson.build | 1 + swaybar/bar.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ swaybar/meson.build | 4 +- 4 files changed, 118 insertions(+), 2 deletions(-) (limited to 'meson.build') diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 1bf2ea2d..0768a683 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -8,14 +8,24 @@ struct swaybar_config; struct swaybar_output; struct swaybar_workspace; +struct swaybar_pointer { + struct wl_pointer *pointer; + struct wl_cursor_theme *cursor_theme; + struct wl_cursor_image *cursor_image; + struct wl_surface *cursor_surface; + struct swaybar_output *current; +}; + struct swaybar { struct wl_display *display; struct wl_compositor *compositor; struct zwlr_layer_shell_v1 *layer_shell; struct wl_shm *shm; + struct wl_seat *seat; struct swaybar_config *config; struct swaybar_output *focused_output; + struct swaybar_pointer pointer; struct status_line *status; int ipc_event_socketfd; diff --git a/meson.build b/meson.build index 49824b30..01788fd9 100644 --- a/meson.build +++ b/meson.build @@ -24,6 +24,7 @@ pcre = dependency('libpcre') wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots']) wayland_server = dependency('wayland-server') wayland_client = dependency('wayland-client') +wayland_cursor = dependency('wayland-cursor') wayland_egl = dependency('wayland-egl') wayland_protos = dependency('wayland-protocols') xkbcommon = dependency('xkbcommon') diff --git a/swaybar/bar.c b/swaybar/bar.c index 0fc41517..e7b8b2ca 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -9,7 +9,13 @@ #include #include #include +#include #include +#ifdef __FreeBSD__ +#include +#else +#include +#endif #include "swaybar/render.h" #include "swaybar/config.h" #include "swaybar/event_loop.h" @@ -56,12 +62,102 @@ struct zwlr_layer_surface_v1_listener layer_surface_listener = { .closed = layer_surface_closed, }; +static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) { + struct swaybar *bar = data; + struct swaybar_pointer *pointer = &bar->pointer; + wl_surface_attach(pointer->cursor_surface, + wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); + wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface, + pointer->cursor_image->hotspot_x, + pointer->cursor_image->hotspot_y); + wl_surface_commit(pointer->cursor_surface); +} + +static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) { + // Who cares +} + +static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + wlr_log(L_DEBUG, "motion"); + // TODO +} + +static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + wlr_log(L_DEBUG, "button"); + // TODO +} + +static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) { + wlr_log(L_DEBUG, "axis"); + // TODO +} + +static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { + // Who cares +} + +static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) { + // Who cares +} + +static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) { + // Who cares +} + +static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t discrete) { + // Who cares +} + +struct wl_pointer_listener pointer_listener = { + .enter = wl_pointer_enter, + .leave = wl_pointer_leave, + .motion = wl_pointer_motion, + .button = wl_pointer_button, + .axis = wl_pointer_axis, + .frame = wl_pointer_frame, + .axis_source = wl_pointer_axis_source, + .axis_stop = wl_pointer_axis_stop, + .axis_discrete = wl_pointer_axis_discrete, +}; + +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) { + struct swaybar *bar = data; + if ((caps & WL_SEAT_CAPABILITY_POINTER)) { + bar->pointer.pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(bar->pointer.pointer, &pointer_listener, bar); + } +} + +static void seat_handle_name(void *data, struct wl_seat *wl_seat, + const char *name) { + // Who cares +} + +const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + .name = seat_handle_name, +}; + static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { struct swaybar *bar = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { bar->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + bar->seat = wl_registry_bind(registry, name, + &wl_seat_interface, 1); + wl_seat_add_listener(bar->seat, &seat_listener, bar); } else if (strcmp(interface, wl_shm_interface.name) == 0) { bar->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); @@ -116,6 +212,15 @@ void bar_setup(struct swaybar *bar, wl_registry_add_listener(registry, ®istry_listener, bar); wl_display_roundtrip(bar->display); assert(bar->compositor && bar->layer_shell && bar->shm); + struct swaybar_pointer *pointer = &bar->pointer; + + assert(pointer->cursor_theme = wl_cursor_theme_load(NULL, 16, bar->shm)); + struct wl_cursor *cursor; + assert(cursor = wl_cursor_theme_get_cursor( + pointer->cursor_theme, "left_ptr")); + pointer->cursor_image = cursor->images[0]; + assert(pointer->cursor_surface = + wl_compositor_create_surface(bar->compositor)); // TODO: we might not necessarily be meant to do all of the outputs struct swaybar_output *output; diff --git a/swaybar/meson.build b/swaybar/meson.build index d15e8b5c..bf6f6d7a 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build @@ -1,6 +1,5 @@ executable( - 'swaybar', - [ + 'swaybar', [ 'bar.c', 'config.c', 'event_loop.c', @@ -20,6 +19,7 @@ executable( pangocairo, rt, wayland_client, + wayland_cursor, wlroots, ], link_with: [lib_sway_common, lib_sway_client], -- cgit v1.2.3 From b32bf595aeae7f8ac68354e45a80c0438374ec17 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 2 Apr 2018 22:48:13 -0400 Subject: Initial swaylock port --- common/background-image.c | 22 +- include/background-image.h | 5 +- meson.build | 2 + swaybg/main.c | 28 +- swaylock/main.c | 952 ++++++++++++--------------------------------- swaylock/meson.build | 18 + 6 files changed, 298 insertions(+), 729 deletions(-) create mode 100644 swaylock/meson.build (limited to 'meson.build') diff --git a/common/background-image.c b/common/background-image.c index 2988c2e2..1a6c0df0 100644 --- a/common/background-image.c +++ b/common/background-image.c @@ -4,6 +4,24 @@ #include "background-image.h" #include "cairo.h" +enum background_mode parse_background_mode(const char *mode) { + if (strcmp(mode, "stretch") == 0) { + return BACKGROUND_MODE_STRETCH; + } else if (strcmp(mode, "fill") == 0) { + return BACKGROUND_MODE_FILL; + } else if (strcmp(mode, "fit") == 0) { + return BACKGROUND_MODE_FIT; + } else if (strcmp(mode, "center") == 0) { + return BACKGROUND_MODE_CENTER; + } else if (strcmp(mode, "tile") == 0) { + return BACKGROUND_MODE_TILE; + } else if (strcmp(mode, "solid_color") == 0) { + return BACKGROUND_MODE_SOLID_COLOR; + } + wlr_log(L_ERROR, "Unsupported background mode: %s", mode); + return BACKGROUND_MODE_INVALID; +} + cairo_surface_t *load_background_image(const char *path) { cairo_surface_t *image; #ifdef HAVE_GDK_PIXBUF @@ -35,8 +53,7 @@ cairo_surface_t *load_background_image(const char *path) { } void render_background_image(cairo_t *cairo, cairo_surface_t *image, - enum background_mode mode, int buffer_width, int buffer_height, - int buffer_scale) { + enum background_mode mode, int buffer_width, int buffer_height) { double width = cairo_image_surface_get_width(image); double height = cairo_image_surface_get_height(image); @@ -93,6 +110,7 @@ void render_background_image(cairo_t *cairo, cairo_surface_t *image, break; } case BACKGROUND_MODE_SOLID_COLOR: + case BACKGROUND_MODE_INVALID: assert(0); break; } diff --git a/include/background-image.h b/include/background-image.h index e160e63d..15935ffd 100644 --- a/include/background-image.h +++ b/include/background-image.h @@ -9,11 +9,12 @@ enum background_mode { BACKGROUND_MODE_CENTER, BACKGROUND_MODE_TILE, BACKGROUND_MODE_SOLID_COLOR, + BACKGROUND_MODE_INVALID, }; +enum background_mode parse_background_mode(const char *mode); cairo_surface_t *load_background_image(const char *path); void render_background_image(cairo_t *cairo, cairo_surface_t *image, - enum background_mode mode, int buffer_width, int buffer_height, - int buffer_scale); + enum background_mode mode, int buffer_width, int buffer_height); #endif diff --git a/meson.build b/meson.build index 01788fd9..d02a446b 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,7 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) pixman = dependency('pixman-1') libcap = dependency('libcap') libinput = dependency('libinput') +libpam = cc.find_library('libpam') math = cc.find_library('m') rt = cc.find_library('rt') git = find_program('git', required: false) @@ -105,6 +106,7 @@ subdir('swaymsg') subdir('client') subdir('swaybg') subdir('swaybar') +subdir('swaylock') config = configuration_data() config.set('sysconfdir', join_paths(prefix, sysconfdir)) diff --git a/swaybg/main.c b/swaybg/main.c index ffefcd08..679b8c20 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -64,15 +64,17 @@ bool is_valid_color(const char *color) { } static void render_frame(struct swaybg_state *state) { - state->current_buffer = get_next_buffer(state->shm, state->buffers, - state->width * state->scale, state->height * state->scale); + int buffer_width = state->width * state->scale, + buffer_height = state->height * state->scale; + state->current_buffer = get_next_buffer(state->shm, + state->buffers, buffer_width, buffer_height); cairo_t *cairo = state->current_buffer->cairo; if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { cairo_set_source_u32(cairo, state->context.color); cairo_paint(cairo); } else { render_background_image(cairo, state->context.image, - state->args->mode, state->width, state->height, state->scale); + state->args->mode, buffer_width, buffer_height); } wl_surface_set_buffer_scale(state->surface, state->scale); @@ -193,24 +195,10 @@ int main(int argc, const char **argv) { args.output_idx = atoi(argv[1]); args.path = argv[2]; - args.mode = BACKGROUND_MODE_STRETCH; - if (strcmp(argv[3], "stretch") == 0) { - args.mode = BACKGROUND_MODE_STRETCH; - } else if (strcmp(argv[3], "fill") == 0) { - args.mode = BACKGROUND_MODE_FILL; - } else if (strcmp(argv[3], "fit") == 0) { - args.mode = BACKGROUND_MODE_FIT; - } else if (strcmp(argv[3], "center") == 0) { - args.mode = BACKGROUND_MODE_CENTER; - } else if (strcmp(argv[3], "tile") == 0) { - args.mode = BACKGROUND_MODE_TILE; - } else if (strcmp(argv[3], "solid_color") == 0) { - args.mode = BACKGROUND_MODE_SOLID_COLOR; - } else { - wlr_log(L_ERROR, "Unsupported background mode: %s", argv[3]); + args.mode = parse_background_mode(argv[3]); + if (args.mode == BACKGROUND_MODE_INVALID) { return 1; } - if (!prepare_context(&state)) { return 1; } @@ -244,10 +232,10 @@ int main(int argc, const char **argv) { zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1); zwlr_layer_surface_v1_add_listener(state.layer_surface, &layer_surface_listener, &state); - state.run_display = true; wl_surface_commit(state.surface); wl_display_roundtrip(state.display); + state.run_display = true; while (wl_display_dispatch(state.display) != -1 && state.run_display) { // This space intentionally left blank } diff --git a/swaylock/main.c b/swaylock/main.c index c2615951..8673694d 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -1,387 +1,235 @@ -#define _XOPEN_SOURCE 500 -#include "wayland-swaylock-client-protocol.h" -#include -#include -#include -#include +#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include +#include +#include #include -#include "client/window.h" -#include "client/registry.h" -#include "client/cairo.h" -#include "swaylock/swaylock.h" -#include "ipc-client.h" -#include "log.h" +#include +#include +#include "background-image.h" +#include "pool-buffer.h" +#include "cairo.h" #include "util.h" - -struct registry *registry; -struct render_data render_data; -struct lock_config *config; -bool show_indicator = true; - -void wl_dispatch_events() { - wl_display_flush(registry->display); - if (wl_display_dispatch(registry->display) == -1) { - sway_log(L_ERROR, "failed to run wl_display_dispatch"); - exit(1); +#include "wlr-layer-shell-unstable-v1-client-protocol.h" + +struct swaylock_args { + uint32_t color; + enum background_mode mode; + bool show_indicator; +}; + +struct swaylock_state { + struct wl_display *display; + struct wl_compositor *compositor; + struct zwlr_layer_shell_v1 *layer_shell; + struct wl_shm *shm; + struct wl_list contexts; + struct swaylock_args args; + bool run_display; +}; + +struct swaylock_context { + cairo_surface_t *image; + struct swaylock_state *state; + struct wl_output *output; + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; + struct pool_buffer buffers[2]; + struct pool_buffer *current_buffer; + uint32_t width, height; + struct wl_list link; +}; + +static void daemonize() { + if (fork() == 0) { + int devnull = open("/dev/null", O_RDWR); + dup2(STDOUT_FILENO, devnull); + dup2(STDERR_FILENO, devnull); + chdir("/"); + } else { + exit(0); } } -void sigalarm_handler(int sig) { - signal(SIGALRM, SIG_IGN); - // Hide typing indicator - render_data.auth_state = AUTH_STATE_IDLE; - render(&render_data, config); - wl_display_flush(registry->display); - signal(SIGALRM, sigalarm_handler); -} - -void sway_terminate(int exit_code) { - int i; - for (i = 0; i < render_data.surfaces->length; ++i) { - struct window *window = render_data.surfaces->items[i]; - window_teardown(window); - } - list_free(render_data.surfaces); - if (registry) { - registry_teardown(registry); +static void render_frame(struct swaylock_context *context) { + struct swaylock_state *state = context->state; + context->current_buffer = get_next_buffer(state->shm, + context->buffers, context->width, context->height); + cairo_t *cairo = context->current_buffer->cairo; + if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { + cairo_set_source_u32(cairo, state->args.color); + cairo_paint(cairo); + } else { + render_background_image(cairo, context->image, + state->args.mode, context->width, context->height); } - exit(exit_code); + wl_surface_attach(context->surface, context->current_buffer->buffer, 0, 0); + wl_surface_damage(context->surface, 0, 0, context->width, context->height); + wl_surface_commit(context->surface); } -char *password; -int password_size; -enum line_source line_source = LINE_SOURCE_DEFAULT; - -struct lock_config *init_config() { - struct lock_config *config = calloc(1, sizeof(struct lock_config)); - - config->font = strdup("sans-serif"); - config->colors.text = 0x000000FF; - - config->colors.line = 0x000000FF; - config->colors.separator = 0x000000FF; - - config->colors.input_cursor = 0x33DB00FF; - config->colors.backspace_cursor = 0xDB3300FF; - - config->colors.normal.inner_ring = 0x000000BF; - config->colors.normal.outer_ring = 0x337D00FF; - - config->colors.validating.inner_ring = 0x0072FFBF; - config->colors.validating.outer_ring = 0x3300FAFF; - - config->colors.invalid.inner_ring = 0xFA0000BF; - config->colors.invalid.outer_ring = 0x7D3300FF; - - config->radius = 50; - config->thickness = 10; - - return config; +static void layer_surface_configure(void *data, + struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t width, uint32_t height) { + struct swaylock_context *context = data; + context->width = width; + context->height = height; + zwlr_layer_surface_v1_ack_configure(surface, serial); + render_frame(context); } -void free_config(struct lock_config *config) { - free(config->font); - free(config); +static void layer_surface_closed(void *data, + struct zwlr_layer_surface_v1 *surface) { + struct swaylock_context *context = data; + zwlr_layer_surface_v1_destroy(context->layer_surface); + wl_surface_destroy(context->surface); + context->state->run_display = false; } -int function_conversation(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr) { - - const char* msg_style_names[] = { - NULL, - "PAM_PROMPT_ECHO_OFF", - "PAM_PROMPT_ECHO_ON", - "PAM_ERROR_MSG", - "PAM_TEXT_INFO", - }; +struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_configure, + .closed = layer_surface_closed, +}; - /* PAM expects an array of responses, one for each message */ - struct pam_response *pam_reply = calloc(num_msg, sizeof(struct pam_response)); - *resp = pam_reply; - - for(int i=0; imsg_style], - msg[i]->msg); - - switch (msg[i]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - case PAM_PROMPT_ECHO_ON: - pam_reply[i].resp = password; - break; +static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) { + wl_pointer_set_cursor(wl_pointer, serial, NULL, 0, 0); +} - case PAM_ERROR_MSG: - case PAM_TEXT_INFO: - break; - } - } +static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) { + // Who cares +} - return PAM_SUCCESS; +static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + // Who cares } -/** - * Note: PAM will free() 'password' during the process - */ -bool verify_password() { - struct passwd *passwd = getpwuid(getuid()); - char *username = passwd->pw_name; - - const struct pam_conv local_conversation = { function_conversation, NULL }; - pam_handle_t *local_auth_handle = NULL; - int pam_err; - if ((pam_err = pam_start("swaylock", username, &local_conversation, &local_auth_handle)) != PAM_SUCCESS) { - sway_abort("PAM returned %d\n", pam_err); - } - if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { - return false; - } - if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) { - return false; - } - return true; +static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + // Who cares } -void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) { - int redraw_screen = 0; - char *password_realloc; - int i; +static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) { + // Who cares +} - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - switch (sym) { - case XKB_KEY_KP_Enter: - case XKB_KEY_Return: - render_data.auth_state = AUTH_STATE_VALIDATING; +static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { + // Who cares +} - render(&render_data, config); - // Make sure our render call will actually be displayed on the screen - wl_dispatch_events(); +static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) { + // Who cares +} - if (verify_password()) { - exit(0); - } +static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) { + // Who cares +} - render_data.auth_state = AUTH_STATE_INVALID; - redraw_screen = 1; +static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t discrete) { + // Who cares +} - password_size = 1024; - password = malloc(password_size); - password[0] = '\0'; - break; - case XKB_KEY_BackSpace: - i = strlen(password); - if (i > 0) { - password[i - 1] = '\0'; - render_data.auth_state = AUTH_STATE_BACKSPACE; - redraw_screen = 1; - } - break; - case XKB_KEY_Control_L: - case XKB_KEY_Control_R: - case XKB_KEY_Shift_L: - case XKB_KEY_Shift_R: - case XKB_KEY_Caps_Lock: - case XKB_KEY_Shift_Lock: - case XKB_KEY_Meta_L: - case XKB_KEY_Meta_R: - case XKB_KEY_Alt_L: - case XKB_KEY_Alt_R: - case XKB_KEY_Super_L: - case XKB_KEY_Super_R: - case XKB_KEY_Hyper_L: - case XKB_KEY_Hyper_R: - break; // don't draw screen on modifier keys - case XKB_KEY_Escape: - case XKB_KEY_u: - case XKB_KEY_U: - // clear password buffer on ctrl-u (or escape for i3lock compatibility) - if (sym == XKB_KEY_Escape || xkb_state_mod_name_is_active(registry->input->xkb.state, - XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) { - render_data.auth_state = AUTH_STATE_BACKSPACE; - redraw_screen = 1; - - password_size = 1024; - free(password); - password = malloc(password_size); - password[0] = '\0'; - break; - } - /* fallthrough */ - default: - render_data.auth_state = AUTH_STATE_INPUT; - redraw_screen = 1; - i = strlen(password); - if (i + 1 == password_size) { - password_size += 1024; - password_realloc = realloc(password, password_size); - // reset password if realloc fails. - if (password_realloc == NULL) { - password_size = 1024; - free(password); - password = malloc(password_size); - password[0] = '\0'; - break; - } else { - password = password_realloc; - } - } - password[i] = (char)codepoint; - password[i + 1] = '\0'; - break; - } - if (redraw_screen) { - render(&render_data, config); - wl_dispatch_events(); - // Hide the indicator after a couple of seconds - alarm(5); - } +struct wl_pointer_listener pointer_listener = { + .enter = wl_pointer_enter, + .leave = wl_pointer_leave, + .motion = wl_pointer_motion, + .button = wl_pointer_button, + .axis = wl_pointer_axis, + .frame = wl_pointer_frame, + .axis_source = wl_pointer_axis_source, + .axis_stop = wl_pointer_axis_stop, + .axis_discrete = wl_pointer_axis_discrete, +}; + +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) { + if ((caps & WL_SEAT_CAPABILITY_POINTER)) { + struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(pointer, &pointer_listener, NULL); } } -void render_color(struct window *window, uint32_t color) { - cairo_set_source_u32(window->cairo, color); - cairo_paint(window->cairo); +static void seat_handle_name(void *data, struct wl_seat *wl_seat, + const char *name) { + // Who cares } -void render_image(struct window *window, cairo_surface_t *image, enum scaling_mode scaling_mode) { - double width = cairo_image_surface_get_width(image); - double height = cairo_image_surface_get_height(image); - int wwidth = window->width * window->scale; - int wheight = window->height * window->scale; - - switch (scaling_mode) { - case SCALING_MODE_STRETCH: - cairo_scale(window->cairo, - (double) wwidth / width, - (double) wheight / height); - cairo_set_source_surface(window->cairo, image, 0, 0); - break; - case SCALING_MODE_FILL: - { - double window_ratio = (double) wwidth / wheight; - double bg_ratio = width / height; - - if (window_ratio > bg_ratio) { - double scale = (double) wwidth / width; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - 0, - (double) wheight/2 / scale - height/2); - } else { - double scale = (double) wheight / height; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 / scale - width/2, - 0); - } - break; - } - case SCALING_MODE_FIT: - { - double window_ratio = (double) wwidth / wheight; - double bg_ratio = width / height; - - if (window_ratio > bg_ratio) { - double scale = (double) wheight / height; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 / scale - width/2, - 0); - } else { - double scale = (double) wwidth / width; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - 0, - (double) wheight/2 / scale - height/2); - } - break; - } - case SCALING_MODE_CENTER: - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 - width/2, - (double) wheight/2 - height/2); - break; - case SCALING_MODE_TILE: - { - cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); - cairo_set_source(window->cairo, pattern); - break; - } +const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + .name = seat_handle_name, +}; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + struct swaylock_state *state = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + state->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 1); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + state->shm = wl_registry_bind(registry, name, + &wl_shm_interface, 1); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + struct wl_seat *seat = wl_registry_bind( + registry, name, &wl_seat_interface, 1); + wl_seat_add_listener(seat, &seat_listener, NULL); + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + state->layer_shell = wl_registry_bind( + registry, name, &zwlr_layer_shell_v1_interface, 1); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + struct swaylock_context *context = + calloc(1, sizeof(struct swaylock_context)); + context->state = state; + context->output = wl_registry_bind(registry, name, + &wl_output_interface, 1); + wl_list_insert(&state->contexts, &context->link); } +} - cairo_paint(window->cairo); +static void handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) { + // who cares } -cairo_surface_t *load_image(char *image_path) { - cairo_surface_t *image = NULL; +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; -#ifdef WITH_GDK_PIXBUF - GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err); - if (!pixbuf) { - sway_abort("Failed to load background image: %s", err->message); - } - image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); - g_object_unref(pixbuf); -#else - image = cairo_image_surface_create_from_png(image_path); -#endif //WITH_GDK_PIXBUF - if (!image) { - sway_abort("Failed to read background image."); - } +static struct swaylock_state state; - return image; +static void sigalarm_handler(int sig) { + signal(SIGALRM, SIG_IGN); + // TODO: Hide typing indicator + signal(SIGALRM, sigalarm_handler); } int main(int argc, char **argv) { - const char *scaling_mode_str = "fit", *socket_path = NULL; - int i; - void *images = NULL; - config = init_config(); - - render_data.num_images = 0; - render_data.color_set = 0; - render_data.color = 0xFFFFFFFF; - render_data.auth_state = AUTH_STATE_IDLE; - - init_log(L_INFO); - // Install SIGALARM handler (for hiding the typing indicator) signal(SIGALRM, sigalarm_handler); static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"color", required_argument, NULL, 'c'}, {"image", required_argument, NULL, 'i'}, - {"scaling", required_argument, NULL, 0}, + {"scaling", required_argument, NULL, 's'}, {"tiling", no_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, {"socket", required_argument, NULL, 'p'}, {"no-unlock-indicator", no_argument, NULL, 'u'}, {"daemonize", no_argument, NULL, 'f'}, - {"font", required_argument, NULL, 0}, - {"line-uses-ring", no_argument, NULL, 'r'}, - {"line-uses-inside", no_argument, NULL, 's'}, - {"textcolor", required_argument, NULL, 0}, - {"insidevercolor", required_argument, NULL, 0}, - {"insidewrongcolor", required_argument, NULL, 0}, - {"insidecolor", required_argument, NULL, 0}, - {"ringvercolor", required_argument, NULL, 0}, - {"ringwrongcolor", required_argument, NULL, 0}, - {"ringcolor", required_argument, NULL, 0}, - {"linecolor", required_argument, NULL, 0}, - {"separatorcolor", required_argument, NULL, 0}, - {"keyhlcolor", required_argument, NULL, 0}, - {"bshlcolor", required_argument, NULL, 0}, - {"indicator-radius", required_argument, NULL, 0}, - {"indicator-thickness", required_argument, NULL, 0}, {0, 0, 0, 0} }; @@ -390,415 +238,109 @@ int main(int argc, char **argv) { "\n" " -h, --help Show help message and quit.\n" " -c, --color Turn the screen into the given color instead of white.\n" - " --scaling Scaling mode: stretch, fill, fit, center, tile.\n" + " -s, --scaling Scaling mode: stretch, fill, fit, center, tile.\n" " -t, --tiling Same as --scaling=tile.\n" " -v, --version Show the version number and quit.\n" " -i, --image [:] Display the given image.\n" " -u, --no-unlock-indicator Disable the unlock indicator.\n" - " -f, --daemonize Detach from the controlling terminal.\n" - " --socket Use the specified socket.\n" - " For more information see `man swaylock`\n"; - + " -f, --daemonize Detach from the controlling terminal.\n" + " --socket Use the specified socket.\n"; - registry = registry_poll(); + struct swaylock_args args = { + .mode = BACKGROUND_MODE_SOLID_COLOR, + .color = 0xFFFFFFFF, + .show_indicator = true, + }; + state.args = args; + wlr_log_init(L_DEBUG, NULL); int c; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "hc:i:srtvuf", long_options, &option_index); + c = getopt_long(argc, argv, "hc:i:s:tvuf", long_options, &option_index); if (c == -1) { break; } switch (c) { - case 'c': - { - render_data.color = parse_color(optarg); - render_data.color_set = 1; + case 'c': { + state.args.color = parse_color(optarg); + state.args.mode = BACKGROUND_MODE_SOLID_COLOR; break; } case 'i': - { - char *image_path = strchr(optarg, ':'); - if (image_path == NULL) { - if (render_data.num_images == 0) { - // Provided image without output - render_data.image = load_image(optarg); - render_data.num_images = -1; - } else { - sway_log(L_ERROR, "output must be defined for all --images or no --images"); - exit(EXIT_FAILURE); - } - } else { - // Provided image for all outputs - if (render_data.num_images == 0) { - images = calloc(registry->outputs->length, sizeof(char*) * 2); - } else if (render_data.num_images == -1) { - sway_log(L_ERROR, "output must be defined for all --images or no --images"); - exit(EXIT_FAILURE); - } - - image_path[0] = '\0'; - ((char**) images)[render_data.num_images * 2] = optarg; - ((char**) images)[render_data.num_images++ * 2 + 1] = ++image_path; + // TODO + return 1; + case 's': + state.args.mode = parse_background_mode(optarg); + if (state.args.mode == BACKGROUND_MODE_INVALID) { + return 1; } break; - } case 't': - scaling_mode_str = "tile"; - break; - case 'p': - socket_path = optarg; + // TODO break; case 'v': - fprintf(stdout, "swaylock version " SWAY_VERSION "\n"); - exit(EXIT_SUCCESS); - break; +#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE + fprintf(stdout, "swaylock version %s (%s, branch \"%s\")\n", + SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH); +#else + fprintf(stdout, "version unknown\n"); +#endif + return 0; case 'u': - show_indicator = false; - break; - case 'f': { - pid_t t = fork(); - if (t == -1) { - sway_log(L_ERROR, "daemon call failed"); - exit(EXIT_FAILURE); - } else if (t > 0) { - exit(0); - } + state.args.show_indicator = false; break; - } - case 'r': - if (line_source != LINE_SOURCE_DEFAULT) { - sway_log(L_ERROR, "line source options conflict"); - exit(EXIT_FAILURE); - } - line_source = LINE_SOURCE_RING; - break; - case 's': - if (line_source != LINE_SOURCE_DEFAULT) { - sway_log(L_ERROR, "line source options conflict"); - exit(EXIT_FAILURE); - } - line_source = LINE_SOURCE_INSIDE; - break; - case 0: - if (strcmp(long_options[option_index].name, "font") == 0) { - free(config->font); - config->font = strdup(optarg); - } else if (strcmp(long_options[option_index].name, "scaling") == 0) { - scaling_mode_str = optarg; - } else if (strcmp(long_options[option_index].name, "textcolor") == 0) { - config->colors.text = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "insidevercolor") == 0) { - config->colors.validating.inner_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "insidewrongcolor") == 0) { - config->colors.invalid.inner_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "insidecolor") == 0) { - config->colors.normal.inner_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "ringvercolor") == 0) { - config->colors.validating.outer_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "ringwrongcolor") == 0) { - config->colors.invalid.outer_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "ringcolor") == 0) { - config->colors.normal.outer_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "linecolor") == 0) { - config->colors.line = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "separatorcolor") == 0) { - config->colors.separator = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "keyhlcolor") == 0) { - config->colors.input_cursor = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "bshlcolor") == 0) { - config->colors.backspace_cursor = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "indicator-radius") == 0) { - config->radius = atoi(optarg); - } else if (strcmp(long_options[option_index].name, "indicator-thickness") == 0) { - config->thickness = atoi(optarg); - } + case 'f': + daemonize(); break; default: fprintf(stderr, "%s", usage); - exit(EXIT_FAILURE); + return 1; } } - render_data.scaling_mode = SCALING_MODE_STRETCH; - if (strcmp(scaling_mode_str, "stretch") == 0) { - render_data.scaling_mode = SCALING_MODE_STRETCH; - } else if (strcmp(scaling_mode_str, "fill") == 0) { - render_data.scaling_mode = SCALING_MODE_FILL; - } else if (strcmp(scaling_mode_str, "fit") == 0) { - render_data.scaling_mode = SCALING_MODE_FIT; - } else if (strcmp(scaling_mode_str, "center") == 0) { - render_data.scaling_mode = SCALING_MODE_CENTER; - } else if (strcmp(scaling_mode_str, "tile") == 0) { - render_data.scaling_mode = SCALING_MODE_TILE; - } else { - sway_abort("Unsupported scaling mode: %s", scaling_mode_str); - } - - password_size = 1024; - password = malloc(password_size); - password[0] = '\0'; - render_data.surfaces = create_list(); - if (!socket_path) { - socket_path = get_socketpath(); - if (!socket_path) { - sway_abort("Unable to retrieve socket path"); - } - } - - if (!registry) { - sway_abort("Unable to connect to wayland compositor"); - } - - if (!registry->swaylock) { - sway_abort("swaylock requires the compositor to support the swaylock extension."); - } + wl_list_init(&state.contexts); - if (registry->pointer) { - // We don't want swaylock to have a pointer - wl_pointer_destroy(registry->pointer); - registry->pointer = NULL; - } - - for (i = 0; i < registry->outputs->length; ++i) { - struct output_state *output = registry->outputs->items[i]; - struct window *window = window_setup(registry, - output->width, output->height, output->scale, true); - if (!window) { - sway_abort("Failed to create surfaces."); - } - list_add(render_data.surfaces, window); - } - - registry->input->notify = notify_key; - - // Different background for the output - if (render_data.num_images >= 1) { - char **displays_paths = images; - render_data.images = calloc(registry->outputs->length, sizeof(cairo_surface_t*)); - - int socketfd = ipc_open_socket(socket_path); - uint32_t len = 0; - char *outputs = ipc_single_command(socketfd, IPC_GET_OUTPUTS, "", &len); - struct json_object *json_outputs = json_tokener_parse(outputs); - - for (i = 0; i < registry->outputs->length; ++i) { - if (displays_paths[i * 2] != NULL) { - for (int j = 0;; ++j) { - if (j >= json_object_array_length(json_outputs)) { - sway_log(L_ERROR, "%s is not an extant output", displays_paths[i * 2]); - exit(EXIT_FAILURE); - } - - struct json_object *dsp_name, *at_j = json_object_array_get_idx(json_outputs, j); - if (!json_object_object_get_ex(at_j, "name", &dsp_name)) { - sway_abort("output doesn't have a name field"); - } - if (!strcmp(displays_paths[i * 2], json_object_get_string(dsp_name))) { - render_data.images[j] = load_image(displays_paths[i * 2 + 1]); - break; - } - } - } - } + assert(state.display = wl_display_connect(NULL)); - json_object_put(json_outputs); - close(socketfd); - free(displays_paths); - } + struct wl_registry *registry = wl_display_get_registry(state.display); + wl_registry_add_listener(registry, ®istry_listener, &state); + wl_display_roundtrip(state.display); + assert(state.compositor && state.layer_shell && state.shm); - render(&render_data, config); - bool locked = false; - while (wl_display_dispatch(registry->display) != -1) { - if (!locked) { - for (i = 0; i < registry->outputs->length; ++i) { - struct output_state *output = registry->outputs->items[i]; - struct window *window = render_data.surfaces->items[i]; - lock_set_lock_surface(registry->swaylock, output->output, window->surface); - } - locked = true; - } + if (wl_list_empty(&state.contexts)) { + wlr_log(L_DEBUG, "Exiting - no outputs to show on."); + return 0; } - // Free surfaces - if (render_data.num_images == -1) { - cairo_surface_destroy(render_data.image); - } else if (render_data.num_images >= 1) { - for (i = 0; i < registry->outputs->length; ++i) { - if (render_data.images[i] != NULL) { - cairo_surface_destroy(render_data.images[i]); - } - } - free(render_data.images); + struct swaylock_context *context; + wl_list_for_each(context, &state.contexts, link) { + assert(context->surface = + wl_compositor_create_surface(state.compositor)); + + context->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + state.layer_shell, context->surface, context->output, + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen"); + assert(context->layer_surface); + + zwlr_layer_surface_v1_set_size(context->layer_surface, 0, 0); + zwlr_layer_surface_v1_set_anchor(context->layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); + zwlr_layer_surface_v1_set_exclusive_zone(context->layer_surface, -1); + zwlr_layer_surface_v1_set_keyboard_interactivity( + context->layer_surface, true); + zwlr_layer_surface_v1_add_listener(context->layer_surface, + &layer_surface_listener, context); + wl_surface_commit(context->surface); + wl_display_roundtrip(state.display); } - for (i = 0; i < render_data.surfaces->length; ++i) { - struct window *window = render_data.surfaces->items[i]; - window_teardown(window); + state.run_display = true; + while (wl_display_dispatch(state.display) != -1 && state.run_display) { + // This space intentionally left blank } - list_free(render_data.surfaces); - registry_teardown(registry); - - free_config(config); - return 0; } - -void render(struct render_data *render_data, struct lock_config *config) { - int i; - for (i = 0; i < render_data->surfaces->length; ++i) { - sway_log(L_DEBUG, "Render surface %d of %d", i, render_data->surfaces->length); - struct window *window = render_data->surfaces->items[i]; - if (!window_prerender(window) || !window->cairo) { - continue; - } - int wwidth = window->width * window->scale; - int wheight = window->height * window->scale; - - cairo_save(window->cairo); - cairo_set_operator(window->cairo, CAIRO_OPERATOR_CLEAR); - cairo_paint(window->cairo); - cairo_restore(window->cairo); - - // Reset the transformation matrix - cairo_identity_matrix(window->cairo); - - if (render_data->num_images == 0 || render_data->color_set) { - render_color(window, render_data->color); - } - - if (render_data->num_images == -1) { - // One background for all - render_image(window, render_data->image, render_data->scaling_mode); - } else if (render_data->num_images >= 1) { - // Different backgrounds - if (render_data->images[i] != NULL) { - render_image(window, render_data->images[i], render_data->scaling_mode); - } - } - - // Reset the transformation matrix again - cairo_identity_matrix(window->cairo); - - // Draw specific values (copied from i3) - const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; - const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; - - // Add visual indicator - if (show_indicator && render_data->auth_state != AUTH_STATE_IDLE) { - // Draw circle - cairo_set_line_width(window->cairo, config->thickness); - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, 0, 2 * M_PI); - switch (render_data->auth_state) { - case AUTH_STATE_INPUT: - case AUTH_STATE_BACKSPACE: { - cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring); - cairo_fill_preserve(window->cairo); - cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring); - cairo_stroke(window->cairo); - } break; - case AUTH_STATE_VALIDATING: { - cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring); - cairo_fill_preserve(window->cairo); - cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring); - cairo_stroke(window->cairo); - } break; - case AUTH_STATE_INVALID: { - cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring); - cairo_fill_preserve(window->cairo); - cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring); - cairo_stroke(window->cairo); - } break; - default: break; - } - - // Draw a message - char *text = NULL; - cairo_set_source_u32(window->cairo, config->colors.text); - cairo_select_font_face(window->cairo, config->font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); - cairo_set_font_size(window->cairo, config->radius/3.0f); - switch (render_data->auth_state) { - case AUTH_STATE_VALIDATING: - text = "verifying"; - break; - case AUTH_STATE_INVALID: - text = "wrong"; - break; - default: break; - } - - if (text) { - cairo_text_extents_t extents; - double x, y; - - cairo_text_extents(window->cairo, text, &extents); - x = wwidth/2 - ((extents.width/2) + extents.x_bearing); - y = wheight/2 - ((extents.height/2) + extents.y_bearing); - - cairo_move_to(window->cairo, x, y); - cairo_show_text(window->cairo, text); - cairo_close_path(window->cairo); - cairo_new_sub_path(window->cairo); - } - - // Typing indicator: Highlight random part on keypress - if (render_data->auth_state == AUTH_STATE_INPUT || render_data->auth_state == AUTH_STATE_BACKSPACE) { - static double highlight_start = 0; - highlight_start += (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_RANGE); - if (render_data->auth_state == AUTH_STATE_INPUT) { - cairo_set_source_u32(window->cairo, config->colors.input_cursor); - } else { - cairo_set_source_u32(window->cairo, config->colors.backspace_cursor); - } - cairo_stroke(window->cairo); - - // Draw borders - cairo_set_source_u32(window->cairo, config->colors.separator); - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_BORDER_THICKNESS); - cairo_stroke(window->cairo); - - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start + TYPE_INDICATOR_RANGE, (highlight_start + TYPE_INDICATOR_RANGE) + TYPE_INDICATOR_BORDER_THICKNESS); - cairo_stroke(window->cairo); - } - - switch(line_source) { - case LINE_SOURCE_RING: - switch(render_data->auth_state) { - case AUTH_STATE_VALIDATING: - cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring); - break; - case AUTH_STATE_INVALID: - cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring); - break; - default: - cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring); - } - break; - case LINE_SOURCE_INSIDE: - switch(render_data->auth_state) { - case AUTH_STATE_VALIDATING: - cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring); - break; - case AUTH_STATE_INVALID: - cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring); - break; - default: - cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring); - break; - } - break; - default: - cairo_set_source_u32(window->cairo, config->colors.line); - break; - } - // Draw inner + outer border of the circle - cairo_set_line_width(window->cairo, 2.0); - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius - config->thickness/2, 0, 2*M_PI); - cairo_stroke(window->cairo); - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius + config->thickness/2, 0, 2*M_PI); - cairo_stroke(window->cairo); - } - window_render(window); - } -} diff --git a/swaylock/meson.build b/swaylock/meson.build new file mode 100644 index 00000000..5b886ded --- /dev/null +++ b/swaylock/meson.build @@ -0,0 +1,18 @@ +executable( + 'swaylock', + 'main.c', + include_directories: [sway_inc], + dependencies: [ + cairo, + client_protos, + gdk_pixbuf, + libpam, + math, + pango, + pangocairo, + wayland_client, + wlroots, + ], + link_with: [lib_sway_common, lib_sway_client], + install: true +) -- cgit v1.2.3 From 842a3ea45c07ab5ed8e3c20af5ef0dc92fff22de Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 5 Apr 2018 15:23:59 -0400 Subject: Add -Wno-unused-result Fixes #1744 --- meson.build | 1 + 1 file changed, 1 insertion(+) (limited to 'meson.build') diff --git a/meson.build b/meson.build index d02a446b..5a37b0e2 100644 --- a/meson.build +++ b/meson.build @@ -11,6 +11,7 @@ project( add_project_arguments('-Wno-unused-parameter', language: 'c') add_project_arguments('-Wno-unused-function', language: 'c') +add_project_arguments('-Wno-unused-result', language: 'c') cc = meson.get_compiler('c') -- cgit v1.2.3 From b24b319bdfb3e94d16950d7fdeadba990bb91a2d Mon Sep 17 00:00:00 2001 From: Bruno Pinto Date: Sun, 8 Apr 2018 00:34:12 +0100 Subject: Improve dependency checks --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'meson.build') diff --git a/meson.build b/meson.build index 5a37b0e2..bf266e5f 100644 --- a/meson.build +++ b/meson.build @@ -35,7 +35,7 @@ pangocairo = dependency('pangocairo') gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) pixman = dependency('pixman-1') libcap = dependency('libcap') -libinput = dependency('libinput') +libinput = dependency('libinput', version: '>=1.6.0') libpam = cc.find_library('libpam') math = cc.find_library('m') rt = cc.find_library('rt') -- cgit v1.2.3