diff --git a/CMake/FindDBus.cmake b/CMake/FindDBus.cmake new file mode 100644 index 00000000..4a1a1805 --- /dev/null +++ b/CMake/FindDBus.cmake @@ -0,0 +1,59 @@ +# - Try to find DBus +# Once done, this will define +# +# DBUS_FOUND - system has DBus +# DBUS_INCLUDE_DIRS - the DBus include directories +# DBUS_LIBRARIES - link these to use DBus +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +FIND_PACKAGE(PkgConfig) +PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1) + +FIND_LIBRARY(DBUS_LIBRARIES + NAMES dbus-1 + HINTS ${PC_DBUS_LIBDIR} + ${PC_DBUS_LIBRARY_DIRS} +) + +FIND_PATH(DBUS_INCLUDE_DIR + NAMES dbus/dbus.h + HINTS ${PC_DBUS_INCLUDEDIR} + ${PC_DBUS_INCLUDE_DIRS} +) + +GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH) +FIND_PATH(DBUS_ARCH_INCLUDE_DIR + NAMES dbus/dbus-arch-deps.h + HINTS ${PC_DBUS_INCLUDEDIR} + ${PC_DBUS_INCLUDE_DIRS} + ${_DBUS_LIBRARY_DIR} + ${DBUS_INCLUDE_DIR} + PATH_SUFFIXES include +) + +SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBUS REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ed45830..c80f6361 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,7 @@ option(enable-swaybar "Enables the swaybar utility" YES) option(enable-swaygrab "Enables the swaygrab utility" YES) option(enable-swaymsg "Enables the swaymsg utility" YES) option(enable-gdk-pixbuf "Use Pixbuf to support more image formats" YES) +option(enable-tray "Enables the swaybar tray" YES) option(zsh-completions "Zsh shell completions" NO) option(default-wallpaper "Installs the default wallpaper" YES) option(LD_LIBRARY_PATH "Configure sway's default LD_LIBRARY_PATH") @@ -64,9 +65,13 @@ find_package(Cairo REQUIRED) find_package(Pango REQUIRED) find_package(GdkPixbuf) find_package(PAM) +find_package(DBus) find_package(LibInput REQUIRED) -find_package(Libcap REQUIRED) + +if (CMAKE_SYSTEM_NAME STREQUAL Linux) + find_package(Libcap REQUIRED) +endif (CMAKE_SYSTEM_NAME STREQUAL Linux) if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) find_package(EpollShim REQUIRED) @@ -87,6 +92,17 @@ else() message(STATUS "Building without gdk-pixbuf, only png images supported.") endif() +if (enable-tray) + if (DBUS_FOUND) + set(ENABLE_TRAY) + add_definitions(-DENABLE_TRAY) + else() + message(WARNING "Tray required but DBus was not found. Tray will not be included") + endif() +else() + message(STATUS "Building without the tray.") +endif() + include_directories(include) add_subdirectory(protocols) diff --git a/HACKING.md b/HACKING.md new file mode 100644 index 00000000..cf18dbb0 --- /dev/null +++ b/HACKING.md @@ -0,0 +1,35 @@ +## Logging + +Use `sway_log(importance, fmt, ...)` to log. The following importances are +available: + +* `L_DEBUG`: Debug messages, only shows with `sway -d` +* `L_INFO`: Informational messages +* `L_ERROR`: Error messages + +`sway_log` is a macro that calls `_sway_log` with the current filename and line +number, which are written into the log with your message. + +## Assertions + +In the compositor, assertions *must not* be fatal. All error cases must be +handled as gracefully as possible - crashing the compositor will make the user +lose all of their work. + +Use `sway_assert(condition, fmt, ...)` to perform an assertion. This returns +`condition`, which you must handle if false. An error will be logged if the +assertion fails. + +Outside of the compositor (swaymsg, swaybar, etc), using `assert.h` is +permitted. + +## Building against a local wlc + +1. Build wlc as described [here](https://github.com/Cloudef/wlc#building) +2. Inside your sway source folder, tell `cmake` to use your local version of wlc: + +```bash +cmake \ + -DWLC_LIBRARIES=path/to/wlc/target/src/libwlc.so \ + -DWLC_INCLUDE_DIRS=path/to/wlc/include . +``` diff --git a/README.md b/README.md index 548fc6ed..65a943d3 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,12 @@ irc.freenode.net). [![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) -[More screenshots](https://github.com/SirCmpwn/sway/wiki/Screenshots-of-Sway) +If you'd like to support Sway development, you can contribute to [my Patreon +page](https://patreon.com/sircmpwn) or you can contribute to +[bounties](https://github.com/SirCmpwn/sway/issues/986) for specific features. +Anyone is welcome to claim a bounty and you can make a bounty for any feature +you wish, and Patreon is more useful for supporting the overall health and +maintenance of Sway. ## Release Signatures @@ -23,8 +28,6 @@ and published [on GitHub](https://github.com/SirCmpwn/sway/releases). - [i3-gaps feature support](https://github.com/SirCmpwn/sway/issues/307) - [security features](https://github.com/SirCmpwn/sway/issues/984) -[Bounties](https://github.com/SirCmpwn/sway/issues/986): sponsor features or get paid to write them - ## Installation ### From Packages diff --git a/common/log.c b/common/log.c index 61937495..af1bdc3f 100644 --- a/common/log.c +++ b/common/log.c @@ -1,4 +1,4 @@ -#define _POSIX_C_SOURCE 1 +#define _POSIX_C_SOURCE 199506L #include #include #include diff --git a/common/stringop.c b/common/stringop.c index 99e9636d..4a37543d 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #include #include #include diff --git a/common/util.c b/common/util.c index a9e6a9c2..d6369853 100644 --- a/common/util.c +++ b/common/util.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #include #include #include @@ -124,38 +124,38 @@ uint32_t parse_color(const char *color) { } char* resolve_path(const char* path) { - struct stat sb; - ssize_t r; - int i; - char *current = NULL; - char *resolved = NULL; - - if(!(current = strdup(path))) { - return NULL; - } - for (i = 0; i < 16; ++i) { - if (lstat(current, &sb) == -1) { - goto failed; - } - if((sb.st_mode & S_IFMT) != S_IFLNK) { - return current; - } - if (!(resolved = malloc(sb.st_size + 1))) { - goto failed; - } - r = readlink(current, resolved, sb.st_size); - if (r == -1 || r > sb.st_size) { - goto failed; - } - resolved[r] = '\0'; - free(current); - current = strdup(resolved); - free(resolved); - resolved = NULL; - } + struct stat sb; + ssize_t r; + int i; + char *current = NULL; + char *resolved = NULL; + + if(!(current = strdup(path))) { + return NULL; + } + for (i = 0; i < 16; ++i) { + if (lstat(current, &sb) == -1) { + goto failed; + } + if((sb.st_mode & S_IFMT) != S_IFLNK) { + return current; + } + if (!(resolved = malloc(sb.st_size + 1))) { + goto failed; + } + r = readlink(current, resolved, sb.st_size); + if (r == -1 || r > sb.st_size) { + goto failed; + } + resolved[r] = '\0'; + free(current); + current = strdup(resolved); + free(resolved); + resolved = NULL; + } failed: - free(resolved); - free(current); - return NULL; -} \ No newline at end of file + free(resolved); + free(current); + return NULL; +} diff --git a/config.in b/config.in index a262fc82..993e2ab6 100644 --- a/config.in +++ b/config.in @@ -41,7 +41,6 @@ output * bg __DATADIR__/sway/Sway_Wallpaper_Blue_1920x1080.png fill # } # # You can get the names of your inputs by running: swaymsg -t get_inputs -# The escape symbol "\" has to be removed. # Read `man 5 sway-input` for more information about this section. ### Key bindings diff --git a/include/client/cairo.h b/include/client/cairo.h index 46c53566..e7ef7c7e 100644 --- a/include/client/cairo.h +++ b/include/client/cairo.h @@ -6,6 +6,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 diff --git a/include/sway/commands.h b/include/sway/commands.h index 078652e7..f67df10f 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -157,17 +157,21 @@ sway_cmd cmd_workspace; sway_cmd cmd_ws_auto_back_and_forth; sway_cmd cmd_workspace_layout; +sway_cmd bar_cmd_activate_button; sway_cmd bar_cmd_binding_mode_indicator; sway_cmd bar_cmd_bindsym; sway_cmd bar_cmd_colors; +sway_cmd bar_cmd_context_button; sway_cmd bar_cmd_font; sway_cmd bar_cmd_mode; sway_cmd bar_cmd_modifier; sway_cmd bar_cmd_output; sway_cmd bar_cmd_height; sway_cmd bar_cmd_hidden_state; +sway_cmd bar_cmd_icon_theme; sway_cmd bar_cmd_id; sway_cmd bar_cmd_position; +sway_cmd bar_cmd_secondary_button; sway_cmd bar_cmd_separator_symbol; sway_cmd bar_cmd_status_command; sway_cmd bar_cmd_pango_markup; diff --git a/include/sway/config.h b/include/sway/config.h index 35f8d5f7..999a471a 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -133,7 +133,17 @@ struct bar_config { char *swaybar_command; char *font; int height; // -1 not defined - int tray_padding; + +#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; diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 697a48c2..010e1f84 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -21,6 +21,9 @@ struct output { struct window *window; struct registry *registry; list_t *workspaces; +#ifdef ENABLE_TRAY + list_t *items; +#endif char *name; int idx; bool focused; @@ -37,6 +40,9 @@ struct workspace { /** Global bar state */ extern struct bar swaybar; +/** True if sway needs to render */ +extern bool dirty; + /** * Setup bar. */ diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 04b12cd4..651f0ee3 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -33,6 +33,17 @@ struct config { 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 { diff --git a/include/swaybar/event_loop.h b/include/swaybar/event_loop.h new file mode 100644 index 00000000..a0cde07f --- /dev/null +++ b/include/swaybar/event_loop.h @@ -0,0 +1,26 @@ +#ifndef _SWAYBAR_EVENT_LOOP_H +#define _SWAYBAR_EVENT_LOOP_H + +#include +#include + +void add_event(int fd, short mask, + void(*cb)(int fd, short mask, void *data), + void *data); + +// Not guaranteed to notify cb immediately +void add_timer(timer_t timer, + void(*cb)(timer_t timer, void *data), + void *data); + +// Returns false if nothing exists, true otherwise +bool remove_event(int fd); + +// Returns false if nothing exists, true otherwise +bool remove_timer(timer_t timer); + +// Blocks and returns after sending callbacks +void event_loop_poll(); + +void init_event_loop(); +#endif /*_SWAYBAR_EVENT_LOOP_H */ diff --git a/include/swaybar/tray/dbus.h b/include/swaybar/tray/dbus.h new file mode 100644 index 00000000..eb9cfea7 --- /dev/null +++ b/include/swaybar/tray/dbus.h @@ -0,0 +1,18 @@ +#ifndef _SWAYBAR_DBUS_H +#define _SWAYBAR_DBUS_H + +#include +#include +extern DBusConnection *conn; + +/** + * Should be called in main loop to dispatch events + */ +void dispatch_dbus(); + +/** + * Initializes async dbus communication + */ +int dbus_init(); + +#endif /* _SWAYBAR_DBUS_H */ diff --git a/include/swaybar/tray/icon.h b/include/swaybar/tray/icon.h new file mode 100644 index 00000000..1cc6ff9c --- /dev/null +++ b/include/swaybar/tray/icon.h @@ -0,0 +1,16 @@ +#ifndef _SWAYBAR_ICON_H +#define _SWAYBAR_ICON_H + +#include +#include +#include + +/** + * Returns the image found by `name` that is closest to `size` + */ +cairo_surface_t *find_icon(const char *name, int size); + +/* Struct used internally only */ +struct subdir; + +#endif /* _SWAYBAR_ICON_H */ diff --git a/include/swaybar/tray/sni.h b/include/swaybar/tray/sni.h new file mode 100644 index 00000000..83809b2d --- /dev/null +++ b/include/swaybar/tray/sni.h @@ -0,0 +1,81 @@ +#ifndef _SWAYBAR_SNI_H +#define _SWAYBAR_SNI_H + +#include +#include + +struct StatusNotifierItem { + /* Name registered to sni watcher */ + char *name; + /* Unique bus name, needed for determining signal origins */ + char *unique_name; + bool kde_special_snowflake; + + cairo_surface_t *image; + bool dirty; +}; + +/* Each output holds an sni_icon_ref of each item to render */ +struct sni_icon_ref { + cairo_surface_t *icon; + struct StatusNotifierItem *ref; +}; + +struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item, + int height); + +void sni_icon_ref_free(struct sni_icon_ref *sni_ref); + +/** + * Will return a new item and get its icon. (see warning below) + */ +struct StatusNotifierItem *sni_create(const char *name); + +/** + * `item` must be a struct StatusNotifierItem * + * `str` must be a NUL terminated char * + * + * Returns 0 if `item` has a name of `str` + */ +int sni_str_cmp(const void *item, const void *str); + +/** + * Returns 0 if `item` has a unique name of `str` or if + * `item->unique_name == NULL` + */ +int sni_uniq_cmp(const void *item, const void *str); + +/** + * Gets an icon for the given item if found. + * + * XXX + * This function keeps a reference to the item until it gets responses, make + * sure that the reference and item are valid during this time. + */ +void get_icon(struct StatusNotifierItem *item); + +/** + * Calls the "activate" method on the given StatusNotifierItem + * + * x and y should be where the item was clicked + */ +void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y); + +/** + * Asks the item to draw a context menu at the given x and y coords + */ +void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y); + +/** + * Calls the "secondary activate" method on the given StatusNotifierItem + * + * x and y should be where the item was clicked + */ +void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y); + +/** + * Deconstructs `item` + */ +void sni_free(struct StatusNotifierItem *item); + +#endif /* _SWAYBAR_SNI_H */ diff --git a/include/swaybar/tray/sni_watcher.h b/include/swaybar/tray/sni_watcher.h new file mode 100644 index 00000000..25ddfcd2 --- /dev/null +++ b/include/swaybar/tray/sni_watcher.h @@ -0,0 +1,10 @@ +#ifndef _SWAYBAR_SNI_WATCHER_H +#define _SWAYBAR_SNI_WATCHER_H + +/** + * Starts the sni_watcher, the watcher is practically a black box and should + * only be accessed though functions described in its spec + */ +int init_sni_watcher(); + +#endif /* _SWAYBAR_SNI_WATCHER_H */ diff --git a/include/swaybar/tray/tray.h b/include/swaybar/tray/tray.h new file mode 100644 index 00000000..b718e555 --- /dev/null +++ b/include/swaybar/tray/tray.h @@ -0,0 +1,32 @@ +#ifndef _SWAYBAR_TRAY_H +#define _SWAYBAR_TRAY_H + +#include +#include +#include "swaybar/tray/dbus.h" +#include "swaybar/tray/sni.h" +#include "swaybar/bar.h" +#include "list.h" + +extern struct tray *tray; + +struct tray { + list_t *items; +}; + +/** + * Processes a mouse event on the bar + */ +void tray_mouse_event(struct output *output, int x, int y, + uint32_t button, uint32_t state); + +uint32_t tray_render(struct output *output, struct config *config); + +void tray_upkeep(struct bar *bar); + +/** + * Initializes the tray with D-Bus + */ +void init_tray(); + +#endif /* _SWAYBAR_TRAY_H */ diff --git a/sway/border.c b/sway/border.c index 10ad92c2..df0022ce 100644 --- a/sway/border.c +++ b/sway/border.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #include #include #include diff --git a/sway/commands.c b/sway/commands.c index 01e5e6b5..f83b5287 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #include #include #include @@ -221,18 +221,22 @@ static struct cmd_handler handlers[] = { }; static struct cmd_handler bar_handlers[] = { + { "activate_button", bar_cmd_activate_button }, { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, { "bindsym", bar_cmd_bindsym }, { "colors", bar_cmd_colors }, + { "context_button", bar_cmd_context_button }, { "font", bar_cmd_font }, { "height", bar_cmd_height }, { "hidden_state", bar_cmd_hidden_state }, + { "icon_theme", bar_cmd_icon_theme }, { "id", bar_cmd_id }, { "mode", bar_cmd_mode }, { "modifier", bar_cmd_modifier }, { "output", bar_cmd_output }, { "pango_markup", bar_cmd_pango_markup }, { "position", bar_cmd_position }, + { "secondary_button", bar_cmd_secondary_button }, { "separator_symbol", bar_cmd_separator_symbol }, { "status_command", bar_cmd_status_command }, { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers }, @@ -250,7 +254,7 @@ static struct cmd_handler bar_handlers[] = { */ struct cmd_results *add_color(const char *name, char *buffer, const char *color) { int len = strlen(color); - if (len != 7 && len != 9 ) { + if (len != 7 && len != 9) { return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color); } diff --git a/sway/commands/assign.c b/sway/commands/assign.c index ec262bb8..c3b03bbc 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #include #include #include "sway/commands.h" @@ -55,4 +55,3 @@ struct cmd_results *cmd_assign(int argc, char **argv) { } return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL); } - diff --git a/sway/commands/bar/activate_button.c b/sway/commands/bar/activate_button.c new file mode 100644 index 00000000..32a1d3e5 --- /dev/null +++ b/sway/commands/bar/activate_button.c @@ -0,0 +1,26 @@ +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_activate_button(int argc, char **argv) { + const char *cmd_name = "activate_button"; +#ifndef ENABLE_TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command " + "%s called, but sway was compiled without tray support", + cmd_name, cmd_name); +#else + struct cmd_results *error = NULL; + if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined."); + } + + // User should be able to prefix with 0x or whatever they want + config->current_bar->secondary_button = strtoul(argv[0], NULL, 0); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +#endif +} diff --git a/sway/commands/bar/context_button.c b/sway/commands/bar/context_button.c new file mode 100644 index 00000000..6d7d7aec --- /dev/null +++ b/sway/commands/bar/context_button.c @@ -0,0 +1,26 @@ +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_context_button(int argc, char **argv) { + const char *cmd_name = "context_button"; +#ifndef ENABLE_TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command " + "%s called, but sway was compiled without tray support", + cmd_name, cmd_name); +#else + struct cmd_results *error = NULL; + if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined."); + } + + // User should be able to prefix with 0x or whatever they want + config->current_bar->context_button = strtoul(argv[0], NULL, 0); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +#endif +} diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c new file mode 100644 index 00000000..cbfc0be5 --- /dev/null +++ b/sway/commands/bar/icon_theme.c @@ -0,0 +1,25 @@ +#define _XOPEN_SOURCE 500 +#include +#include "sway/commands.h" + +struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) { + const char *cmd_name = "tray_output"; +#ifndef ENABLE_TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command " + "%s called, but sway was compiled without tray support", + cmd_name, cmd_name); +#else + struct cmd_results *error = NULL; + if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined."); + } + + config->current_bar->icon_theme = strdup(argv[0]); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +#endif +} diff --git a/sway/commands/bar/secondary_button.c b/sway/commands/bar/secondary_button.c new file mode 100644 index 00000000..745045c5 --- /dev/null +++ b/sway/commands/bar/secondary_button.c @@ -0,0 +1,26 @@ +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) { + const char *cmd_name = "secondary_button"; +#ifndef ENABLE_TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command " + "%s called, but sway was compiled without tray support", + cmd_name, cmd_name); +#else + struct cmd_results *error = NULL; + if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined."); + } + + // User should be able to prefix with 0x or whatever they want + config->current_bar->secondary_button = strtoul(argv[0], NULL, 0); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +#endif +} diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c index 8a1b5d35..012304a9 100644 --- a/sway/commands/bar/tray_output.c +++ b/sway/commands/bar/tray_output.c @@ -1,7 +1,29 @@ +#define _XOPEN_SOURCE 500 +#include #include "sway/commands.h" -#include "log.h" struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { - sway_log(L_ERROR, "Warning: tray_output is not supported on wayland"); + const char *cmd_name = "tray_output"; +#ifndef ENABLE_TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command " + "%s called, but sway was compiled without tray support", + cmd_name, cmd_name); +#else + struct cmd_results *error = NULL; + if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined."); + } + + if (strcmp(argv[0], "all") == 0) { + // Default behaviour + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } + config->current_bar->tray_output = strdup(argv[0]); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +#endif } diff --git a/sway/commands/bar/tray_padding.c b/sway/commands/bar/tray_padding.c index 8c559f65..ac0572ce 100644 --- a/sway/commands/bar/tray_padding.c +++ b/sway/commands/bar/tray_padding.c @@ -1,30 +1,34 @@ #include -#include #include #include "sway/commands.h" #include "log.h" struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) { + const char *cmd_name = "tray_padding"; +#ifndef ENABLE_TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command" + "%s called, but sway was compiled without tray support", + cmd_name, cmd_name); +#else struct cmd_results *error = NULL; - if ((error = checkarg(argc, "tray_padding", EXPECTED_AT_LEAST, 1))) { + if ((error = checkarg(argc, cmd_name, EXPECTED_AT_LEAST, 1))) { return error; } if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "tray_padding", "No bar defined."); + return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined."); } - int padding = atoi(argv[0]); - if (padding < 0) { - return cmd_results_new(CMD_INVALID, "tray_padding", - "Invalid padding value %s, minimum is 0", argv[0]); + if (argc == 1 || (argc == 2 && strcasecmp("px", argv[1]) == 0)) { + char *inv; + uint32_t padding = strtoul(argv[0], &inv, 10); + if (*inv == '\0' || strcasecmp(inv, "px") == 0) { + config->current_bar->tray_padding = padding; + sway_log(L_DEBUG, "Enabling tray padding of %d px on bar: %s", padding, config->current_bar->id); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } } - - if (argc > 1 && strcasecmp("px", argv[1]) != 0) { - return cmd_results_new(CMD_INVALID, "tray_padding", - "Unknown unit %s", argv[1]); - } - config->current_bar->tray_padding = padding; - sway_log(L_DEBUG, "Enabling tray padding of %d px on bar: %s", padding, config->current_bar->id); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + return cmd_results_new(CMD_FAILURE, cmd_name, + "Expected 'tray_padding [px]'"); +#endif } diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 8282277b..af5a01e5 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -1,7 +1,9 @@ #include #include +#include #include "sway/commands.h" #include "sway/config.h" +#include "sway/input_state.h" #include "list.h" #include "log.h" #include "stringop.h" @@ -52,6 +54,12 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { // Check for xkb key xkb_keysym_t sym = xkb_keysym_from_name(split->items[i], XKB_KEYSYM_CASE_INSENSITIVE); + + // Check for mouse binding + if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && + strlen(split->items[i]) == strlen("button0")) { + sym = ((char *)split->items[i])[strlen("button")] - '1' + M_LEFT_CLICK; + } if (!sym) { free_sway_binding(binding); free_flat_list(split); diff --git a/sway/commands/client.c b/sway/commands/client.c index 7954f670..f3d879cd 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c @@ -4,26 +4,30 @@ static struct cmd_results *parse_border_color(struct border_colors *border_colors, const char *cmd_name, int argc, char **argv) { struct cmd_results *error = NULL; - if (argc != 5) { - return cmd_results_new(CMD_INVALID, cmd_name, "Requires exactly five color values"); + if (argc < 3 || argc > 5) { + return cmd_results_new(CMD_INVALID, cmd_name, "Requires between three and five color values"); } - uint32_t colors[5]; + uint32_t *colors[5] = { + &border_colors->border, + &border_colors->background, + &border_colors->text, + &border_colors->indicator, + &border_colors->child_border + }; int i; - for (i = 0; i < 5; i++) { + for (i = 0; i < argc; i++) { char buffer[10]; error = add_color(cmd_name, buffer, argv[i]); if (error) { return error; } - colors[i] = strtoul(buffer+1, NULL, 16); + *colors[i] = strtoul(buffer + 1, NULL, 16); } - border_colors->border = colors[0]; - border_colors->background = colors[1]; - border_colors->text = colors[2]; - border_colors->indicator = colors[3]; - border_colors->child_border = colors[4]; + if (argc < 5) { + border_colors->child_border = border_colors->background; + } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 40ebd590..57a86565 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -85,6 +85,10 @@ static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char ** enum swayc_layouts old_layout = container->layout; enum swayc_layouts layout = old_layout; + if ((error = checkarg(argc, "layout auto", EXPECTED_MORE_THAN, 1))) { + return error; + } + if (strcasecmp(argv[1], "left") == 0) { layout = L_AUTO_LEFT; } else if (strcasecmp(argv[1], "right") == 0) { diff --git a/sway/commands/set.c b/sway/commands/set.c index 1d6bce04..e3d08dd3 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #include #include #include diff --git a/sway/config.c b/sway/config.c index 19b1882f..e0b65615 100644 --- a/sway/config.c +++ b/sway/config.c @@ -1,5 +1,5 @@ #define _POSIX_C_SOURCE 200809L -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #include #include #include @@ -69,6 +69,10 @@ static void free_bar(struct bar_config *bar) { } free(bar->mode); free(bar->hidden_state); +#ifdef ENABLE_TRAY + free(bar->tray_output); + free(bar->icon_theme); +#endif free(bar->status_command); free(bar->font); free(bar->separator_symbol); @@ -527,7 +531,7 @@ bool load_main_config(const char *file, bool is_active) { list_add(config->config_chain, path); config->reading = true; - + // Read security configs bool success = true; DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); @@ -1386,7 +1390,14 @@ struct bar_config *default_bar_config(void) { bar->separator_symbol = NULL; bar->strip_workspace_numbers = false; bar->binding_mode_indicator = true; +#ifdef ENABLE_TRAY + bar->tray_output = NULL; + bar->icon_theme = NULL; bar->tray_padding = 2; + bar->activate_button = 0x110; /* BTN_LEFT */ + bar->context_button = 0x111; /* BTN_RIGHT */ + bar->secondary_button = 0x112; /* BTN_MIDDLE */ +#endif bar->verbose = false; bar->pid = 0; // set default colors diff --git a/sway/container.c b/sway/container.c index 08aa77a8..358ba767 100644 --- a/sway/container.c +++ b/sway/container.c @@ -699,12 +699,12 @@ static bool pointer_test(swayc_t *view, void *_origin) { swayc_t *container_under_pointer(void) { // root.output->workspace - if (!root_container.focused || !root_container.focused->focused) { + if (!root_container.focused) { return NULL; } - swayc_t *lookup = root_container.focused->focused; + swayc_t *lookup = root_container.focused; // Case of empty workspace - if (lookup->children == 0) { + if (lookup->children && !lookup->unmanaged) { return NULL; } struct wlc_point origin; @@ -712,6 +712,17 @@ swayc_t *container_under_pointer(void) { while (lookup && lookup->type != C_VIEW) { int i; int len; + for (int _i = 0; lookup->unmanaged && _i < lookup->unmanaged->length; ++_i) { + wlc_handle *handle = lookup->unmanaged->items[_i]; + const struct wlc_geometry *geo = wlc_view_get_geometry(*handle); + if (origin.x >= geo->origin.x && origin.y >= geo->origin.y + && origin.x < geo->origin.x + (int)geo->size.w + && origin.y < geo->origin.y + (int)geo->size.h) { + // Hack: we force focus upon unmanaged views here + wlc_view_focus(*handle); + return NULL; + } + } // if tabbed/stacked go directly to focused container, otherwise search // children if (lookup->layout == L_TABBED || lookup->layout == L_STACKED) { diff --git a/sway/criteria.c b/sway/criteria.c index 1ea8311e..04683f66 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #include #include #include diff --git a/sway/handlers.c b/sway/handlers.c index 0aa35c26..39261e3d 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -609,6 +609,8 @@ static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geo view->y = geometry->origin.y; update_geometry(view); } + } else { + wlc_view_set_geometry(handle, 0, geometry); } } @@ -927,6 +929,26 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w // Update view pointer is on pointer_state.view = container_under_pointer(); + struct sway_mode *mode = config->current_mode; + // handle bindings + for (int i = 0; i < mode->bindings->length; ++i) { + struct sway_binding *binding = mode->bindings->items[i]; + if ((modifiers->mods ^ binding->modifiers) == 0) { + switch (state) { + case WLC_BUTTON_STATE_PRESSED: { + if (!binding->release && handle_bindsym(binding, button, 0)) { + return EVENT_HANDLED; + } + } + case WLC_BUTTON_STATE_RELEASED: + if (binding->release && handle_bindsym(binding, button, 0)) { + return EVENT_HANDLED; + } + break; + } + } + } + // Update pointer_state switch (button) { case M_LEFT_CLICK: diff --git a/sway/input.c b/sway/input.c index 4f1ef760..6263f79f 100644 --- a/sway/input.c +++ b/sway/input.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #include #include #include @@ -60,7 +60,7 @@ char *libinput_dev_unique_id(struct libinput_device *device) { } const char *fmt = "%d:%d:%s"; - snprintf(identifier, len, fmt, vendor, product, name); + snprintf(identifier, len, fmt, vendor, product, name); free(name); return identifier; } diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 512144a4..31de53f0 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -327,7 +327,22 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { json_object *json = json_object_new_object(); json_object_object_add(json, "id", json_object_new_string(bar->id)); - json_object_object_add(json, "tray_output", NULL); +#ifdef ENABLE_TRAY + if (bar->tray_output) { + json_object_object_add(json, "tray_output", json_object_new_string(bar->tray_output)); + } else { + json_object_object_add(json, "tray_output", NULL); + } + if (bar->icon_theme) { + json_object_object_add(json, "icon_theme", json_object_new_string(bar->icon_theme)); + } else { + json_object_object_add(json, "icon_theme", NULL); + } + json_object_object_add(json, "tray_padding", json_object_new_int(bar->tray_padding)); + json_object_object_add(json, "activate_button", json_object_new_int(bar->activate_button)); + json_object_object_add(json, "context_button", json_object_new_int(bar->context_button)); + json_object_object_add(json, "secondary_button", json_object_new_int(bar->secondary_button)); +#endif json_object_object_add(json, "mode", json_object_new_string(bar->mode)); json_object_object_add(json, "hidden_state", json_object_new_string(bar->hidden_state)); json_object_object_add(json, "modifier", json_object_new_string(get_modifier_name_by_mask(bar->modifier))); diff --git a/sway/main.c b/sway/main.c index b9549b12..82375e0b 100644 --- a/sway/main.c +++ b/sway/main.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 200112L #include #include @@ -27,6 +27,7 @@ #include "stringop.h" #include "sway.h" #include "log.h" +#include "util.h" static bool terminate_request = false; static int exit_value = 0; @@ -209,6 +210,27 @@ static void security_sanity_check() { #endif } +static void executable_sanity_check() { +#ifdef __linux__ + struct stat sb; + char *exe = realpath("/proc/self/exe", NULL); + stat(exe, &sb); + // We assume that cap_get_file returning NULL implies ENODATA + if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { + sway_log(L_ERROR, + "sway executable has both the s(g)uid bit AND file caps set."); + sway_log(L_ERROR, + "This is strongly discouraged (and completely broken)."); + sway_log(L_ERROR, + "Please clear one of them (either the suid bit, or the file caps)."); + sway_log(L_ERROR, + "If unsure, strip the file caps."); + exit(EXIT_FAILURE); + } + free(exe); +#endif +} + int main(int argc, char **argv) { static int verbose = 0, debug = 0, validate = 0; @@ -288,6 +310,15 @@ int main(int argc, char **argv) { } } + // we need to setup logging before wlc_init in case it fails. + if (debug) { + init_log(L_DEBUG); + } else if (verbose || validate) { + init_log(L_INFO); + } else { + init_log(L_ERROR); + } + if (optind < argc) { // Behave as IPC client if(optind != 1) { sway_log(L_ERROR, "Don't use options with the IPC client"); @@ -317,6 +348,7 @@ int main(int argc, char **argv) { return 0; } + executable_sanity_check(); #ifdef __linux__ bool suid = false; if (getuid() != geteuid() || getgid() != getegid()) { @@ -329,14 +361,6 @@ int main(int argc, char **argv) { } #endif - // we need to setup logging before wlc_init in case it fails. - if (debug) { - init_log(L_DEBUG); - } else if (verbose || validate) { - init_log(L_INFO); - } else { - init_log(L_ERROR); - } wlc_log_set_handler(wlc_log_handler); log_kernel(); log_distro(); @@ -409,4 +433,3 @@ int main(int argc, char **argv) { return exit_value; } - diff --git a/sway/security.c b/sway/security.c index 8eab6126..92de06c1 100644 --- a/sway/security.c +++ b/sway/security.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 #include #include #include diff --git a/sway/sway-bar.5.txt b/sway/sway-bar.5.txt index 5a52e7db..29487662 100644 --- a/sway/sway-bar.5.txt +++ b/sway/sway-bar.5.txt @@ -65,6 +65,42 @@ Commands **height** :: Sets the height of the bar. Default height will match the font size. +Tray +---- + +Swaybar provides a system tray where programs such as NetworkManager, VLC, +Pidgin, etc. can place little icons. The following commands configure +interaction with the tray or individual icons. +The _button_ argument in all following commands is a Linux input event code as +defined in linux/input-event-codes.h. This is because wayland defines button +codes in this manner. + +**activate_button**