Merge branch 'master' into pretty-print-swaymsg

This commit is contained in:
Drew DeVault 2017-04-10 07:17:47 -04:00 committed by GitHub
commit 5d3a02a7c6
28 changed files with 424 additions and 84 deletions

View file

@ -18,6 +18,7 @@ arch:
- cairo - cairo
- gdk-pixbuf2 - gdk-pixbuf2
- wlc-git - wlc-git
- libcap
script: script:
- "cmake ." - "cmake ."
- "make" - "make"

56
CMake/FindLibcap.cmake Normal file
View file

@ -0,0 +1,56 @@
#.rst:
# FindLibcap
# -------
#
# Find Libcap library
#
# Try to find Libcap library. The following values are defined
#
# ::
#
# Libcap_FOUND - True if Libcap is available
# Libcap_INCLUDE_DIRS - Include directories for Libcap
# Libcap_LIBRARIES - List of libraries for Libcap
# Libcap_DEFINITIONS - List of definitions for Libcap
#
# and also the following more fine grained variables
#
# ::
#
# Libcap_VERSION
# Libcap_VERSION_MAJOR
# Libcap_VERSION_MINOR
#
#=============================================================================
# Copyright (c) 2017 Jerzi Kaminsky
#
# Distributed under the OSI-approved BSD License (the "License");
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
include(FeatureSummary)
set_package_properties(Libcap PROPERTIES
URL "https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2"
DESCRIPTION "Library for getting and setting POSIX.1e capabilities")
find_package(PkgConfig)
pkg_check_modules(PC_CAP QUIET Libcap)
find_library(Libcap_LIBRARIES NAMES cap HINTS ${PC_CAP_LIBRARY_DIRS})
find_path(Libcap_INCLUDE_DIRS sys/capability.h HINTS ${PC_CAP_INCLUDE_DIRS})
set(Libcap_VERSION ${PC_CAP_VERSION})
string(REPLACE "." ";" VERSION_LIST "${PC_CAP_VERSION}")
LIST(LENGTH VERSION_LIST n)
if (n EQUAL 2)
list(GET VERSION_LIST 0 Libcap_VERSION_MAJOR)
list(GET VERSION_LIST 1 Libcap_VERSION_MINOR)
endif ()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Libcap DEFAULT_MSG Libcap_INCLUDE_DIRS Libcap_LIBRARIES)
mark_as_advanced(Libcap_INCLUDE_DIRS Libcap_LIBRARIES Libcap_DEFINITIONS
Libcap_VERSION Libcap_VERSION_MAJOR Libcap_VERSION_MICRO Libcap_VERSION_MINOR)

View file

@ -66,6 +66,7 @@ find_package(GdkPixbuf)
find_package(PAM) find_package(PAM)
find_package(LibInput REQUIRED) find_package(LibInput REQUIRED)
find_package(Libcap REQUIRED)
if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
find_package(EpollShim REQUIRED) find_package(EpollShim REQUIRED)

View file

@ -5,6 +5,9 @@
#include <wlc/wlc.h> #include <wlc/wlc.h>
#include "config.h" #include "config.h"
// Container that a called command should act upon. Only valid in command functions.
extern swayc_t *current_container;
/** /**
* Indicates the result of a command's execution. * Indicates the result of a command's execution.
*/ */
@ -126,6 +129,7 @@ sway_cmd cmd_ipc;
sway_cmd cmd_kill; sway_cmd cmd_kill;
sway_cmd cmd_layout; sway_cmd cmd_layout;
sway_cmd cmd_log_colors; sway_cmd cmd_log_colors;
sway_cmd cmd_mark;
sway_cmd cmd_mode; sway_cmd cmd_mode;
sway_cmd cmd_mouse_warping; sway_cmd cmd_mouse_warping;
sway_cmd cmd_move; sway_cmd cmd_move;
@ -140,12 +144,14 @@ sway_cmd cmd_resize;
sway_cmd cmd_scratchpad; sway_cmd cmd_scratchpad;
sway_cmd cmd_seamless_mouse; sway_cmd cmd_seamless_mouse;
sway_cmd cmd_set; sway_cmd cmd_set;
sway_cmd cmd_show_marks;
sway_cmd cmd_smart_gaps; sway_cmd cmd_smart_gaps;
sway_cmd cmd_split; sway_cmd cmd_split;
sway_cmd cmd_splith; sway_cmd cmd_splith;
sway_cmd cmd_splitt; sway_cmd cmd_splitt;
sway_cmd cmd_splitv; sway_cmd cmd_splitv;
sway_cmd cmd_sticky; sway_cmd cmd_sticky;
sway_cmd cmd_unmark;
sway_cmd cmd_workspace; sway_cmd cmd_workspace;
sway_cmd cmd_ws_auto_back_and_forth; sway_cmd cmd_ws_auto_back_and_forth;
sway_cmd cmd_workspace_layout; sway_cmd cmd_workspace_layout;

View file

@ -275,6 +275,7 @@ struct sway_config {
bool reading; bool reading;
bool auto_back_and_forth; bool auto_back_and_forth;
bool seamless_mouse; bool seamless_mouse;
bool show_marks;
bool edge_gaps; bool edge_gaps;
bool smart_gaps; bool smart_gaps;

View file

@ -165,6 +165,11 @@ struct sway_container {
* Number of slave groups (e.g. columns) in auto layouts. * Number of slave groups (e.g. columns) in auto layouts.
*/ */
size_t nb_slave_groups; size_t nb_slave_groups;
/**
* Marks applied to the container, list_t of char*.
*/
list_t *marks;
}; };
enum visibility_mask { enum visibility_mask {

View file

@ -33,4 +33,7 @@ char *extract_crit_tokens(list_t *tokens, const char *criteria);
// been set with `for_window` commands and have an associated cmdlist. // been set with `for_window` commands and have an associated cmdlist.
list_t *criteria_for(swayc_t *cont); list_t *criteria_for(swayc_t *cont);
// Returns a list of all containers that match the given list of tokens.
list_t *container_for(list_t *tokens);
#endif #endif

View file

@ -1,8 +1,11 @@
#define _XOPEN_SOURCE 500
#include <wlc/wlc-render.h> #include <wlc/wlc-render.h>
#include <cairo/cairo.h> #include <cairo/cairo.h>
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "sway/border.h" #include "sway/border.h"
#include "sway/container.h" #include "sway/container.h"
@ -190,6 +193,26 @@ static void render_title_bar(swayc_t *view, cairo_t *cr, struct wlc_geometry *b,
cairo_set_source_u32(cr, colors->text); cairo_set_source_u32(cr, colors->text);
pango_printf(cr, config->font, 1, false, "%s", view->name); pango_printf(cr, config->font, 1, false, "%s", view->name);
} }
// Marks
if (config->show_marks && view->marks) {
int total_len = 0;
for(int i = view->marks->length - 1; i >= 0; --i) {
char *mark = (char *)view->marks->items[i];
if (*mark != '_') {
int width, height;
get_text_size(cr, config->font, &width, &height, 1, false, "[%s]", mark);
total_len += width;
if ((int)tb->size.w + x - (total_len + 2) < x + 2) {
break;
} else {
cairo_move_to(cr, (int)tb->size.w + x - (total_len + 2), y + 2);
cairo_set_source_u32(cr, colors->text);
pango_printf(cr, config->font, 1, false, "[%s]", mark);
}
}
}
}
// titlebars has a border all around for tabbed layouts // titlebars has a border all around for tabbed layouts
if (view->parent->layout == L_TABBED) { if (view->parent->layout == L_TABBED) {

View file

@ -43,6 +43,8 @@ struct cmd_handler {
int sp_index = 0; int sp_index = 0;
swayc_t *current_container = NULL;
// Returns error object, or NULL if check succeeds. // Returns error object, or NULL if check succeeds.
struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) { struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
@ -190,6 +192,7 @@ static struct cmd_handler handlers[] = {
{ "kill", cmd_kill }, { "kill", cmd_kill },
{ "layout", cmd_layout }, { "layout", cmd_layout },
{ "log_colors", cmd_log_colors }, { "log_colors", cmd_log_colors },
{ "mark", cmd_mark },
{ "mode", cmd_mode }, { "mode", cmd_mode },
{ "mouse_warping", cmd_mouse_warping }, { "mouse_warping", cmd_mouse_warping },
{ "move", cmd_move }, { "move", cmd_move },
@ -203,12 +206,14 @@ static struct cmd_handler handlers[] = {
{ "scratchpad", cmd_scratchpad }, { "scratchpad", cmd_scratchpad },
{ "seamless_mouse", cmd_seamless_mouse }, { "seamless_mouse", cmd_seamless_mouse },
{ "set", cmd_set }, { "set", cmd_set },
{ "show_marks", cmd_show_marks },
{ "smart_gaps", cmd_smart_gaps }, { "smart_gaps", cmd_smart_gaps },
{ "split", cmd_split }, { "split", cmd_split },
{ "splith", cmd_splith }, { "splith", cmd_splith },
{ "splitt", cmd_splitt }, { "splitt", cmd_splitt },
{ "splitv", cmd_splitv }, { "splitv", cmd_splitv },
{ "sticky", cmd_sticky }, { "sticky", cmd_sticky },
{ "unmark", cmd_unmark },
{ "workspace", cmd_workspace }, { "workspace", cmd_workspace },
{ "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
{ "workspace_layout", cmd_workspace_layout }, { "workspace_layout", cmd_workspace_layout },
@ -368,42 +373,37 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
char *head = exec; char *head = exec;
char *cmdlist; char *cmdlist;
char *cmd; char *cmd;
char *criteria __attribute__((unused)); list_t *containers = NULL;
head = exec; head = exec;
do { do {
// Extract criteria (valid for this command list only). // Extract criteria (valid for this command list only).
criteria = NULL;
if (*head == '[') { if (*head == '[') {
++head; ++head;
criteria = argsep(&head, "]"); char *criteria_string = argsep(&head, "]");
if (head) { if (head) {
++head; ++head;
// TODO handle criteria list_t *tokens = create_list();
char *error;
if ((error = extract_crit_tokens(tokens, criteria_string))) {
results = cmd_results_new(CMD_INVALID, criteria_string,
"Can't parse criteria string: %s", error);
free(error);
free(tokens);
goto cleanup;
}
containers = container_for(tokens);
free(tokens);
} else { } else {
if (!results) { if (!results) {
results = cmd_results_new(CMD_INVALID, criteria, "Unmatched ["); results = cmd_results_new(CMD_INVALID, criteria_string, "Unmatched [");
} }
goto cleanup; goto cleanup;
} }
// Skip leading whitespace // Skip leading whitespace
head += strspn(head, whitespace); head += strspn(head, whitespace);
// TODO: it will yield unexpected results to execute commands
// (on any view) that where meant for certain views only.
if (!results) {
int len = strlen(criteria) + strlen(head) + 4;
char *tmp = malloc(len);
if (tmp) {
snprintf(tmp, len, "[%s] %s", criteria, head);
} else {
sway_log(L_DEBUG, "Unable to allocate criteria string for cmd result");
}
results = cmd_results_new(CMD_INVALID, tmp,
"Can't handle criteria string: Refusing to execute command");
free(tmp);
}
goto cleanup;
} }
// Split command list // Split command list
cmdlist = argsep(&head, ";"); cmdlist = argsep(&head, ";");
@ -447,21 +447,43 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
free_argv(argc, argv); free_argv(argc, argv);
goto cleanup; goto cleanup;
} }
struct cmd_results *res = handler->handle(argc-1, argv+1); int i = 0;
if (res->status != CMD_SUCCESS) { do {
free_argv(argc, argv); if (!containers) {
if (results) { current_container = get_focused_container(&root_container);
free_cmd_results(results); } else if (containers->length == 0) {
break;
} else {
current_container = (swayc_t *)containers->items[i];
} }
results = res; sway_log(L_INFO, "Running on container '%s'", current_container->name);
goto cleanup;
} struct cmd_results *res = handler->handle(argc-1, argv+1);
if (res->status != CMD_SUCCESS) {
free_argv(argc, argv);
if (results) {
free_cmd_results(results);
}
results = res;
goto cleanup;
}
free_cmd_results(res);
++i;
} while(containers && i < containers->length);
free_argv(argc, argv); free_argv(argc, argv);
free_cmd_results(res);
} while(cmdlist); } while(cmdlist);
if (containers) {
list_free(containers);
containers = NULL;
}
} while(head); } while(head);
cleanup: cleanup:
free(exec); free(exec);
if (containers) {
free(containers);
}
if (!results) { if (!results) {
results = cmd_results_new(CMD_SUCCESS, NULL, NULL); results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

View file

@ -20,7 +20,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
"Expected 'border <normal|pixel|none|toggle> [<n>]"); "Expected 'border <normal|pixel|none|toggle> [<n>]");
} }
swayc_t *view = get_focused_view(&root_container); swayc_t *view = current_container;
enum swayc_border_types border = view->border_type; enum swayc_border_types border = view->border_type;
int thickness = view->border_thickness; int thickness = view->border_thickness;

View file

@ -13,7 +13,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) { if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
return error; return error;
} }
swayc_t *view = get_focused_container(&root_container); swayc_t *view = current_container;
bool wants_floating; bool wants_floating;
if (strcasecmp(argv[0], "enable") == 0) { if (strcasecmp(argv[0], "enable") == 0) {
wants_floating = true; wants_floating = true;

View file

@ -30,6 +30,9 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
} }
} }
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else if (argc == 0) {
set_focused_container(current_container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) { } else if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) {
return error; return error;
} }

View file

@ -14,7 +14,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) { if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) {
return error; return error;
} }
swayc_t *container = get_focused_view(&root_container); swayc_t *container = current_container;
if(container->type != C_VIEW){ if(container->type != C_VIEW){
return cmd_results_new(CMD_INVALID, "fullscreen", "Only views can fullscreen"); return cmd_results_new(CMD_INVALID, "fullscreen", "Only views can fullscreen");
} }

View file

@ -6,7 +6,7 @@ struct cmd_results *cmd_kill(int argc, char **argv) {
if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file."); if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file.");
if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running."); if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running.");
swayc_t *container = get_focused_container(&root_container); swayc_t *container = current_container;
close_views(container); close_views(container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

View file

@ -16,7 +16,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
return error; return error;
} }
swayc_t *parent = get_focused_container(&root_container); swayc_t *parent = current_container;
if (parent->is_floating) { if (parent->is_floating) {
return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows"); return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows");
} }

87
sway/commands/mark.c Normal file
View file

@ -0,0 +1,87 @@
#include <string.h>
#include <strings.h>
#include <stdbool.h>
#include "sway/commands.h"
#include "list.h"
#include "stringop.h"
static void find_marks_callback(swayc_t *container, void *_mark) {
char *mark = (char *)_mark;
int index;
if (container->marks && ((index = list_seq_find(container->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1)) {
list_del(container->marks, index);
}
}
struct cmd_results *cmd_mark(int argc, char **argv) {
struct cmd_results *error = NULL;
if (config->reading) return cmd_results_new(CMD_FAILURE, "mark", "Can't be used in config file.");
if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) {
return error;
}
swayc_t *view = current_container;
bool add = false;
bool toggle = false;
if (strcmp(argv[0], "--add") == 0) {
--argc; ++argv;
add = true;
} else if (strcmp(argv[0], "--replace") == 0) {
--argc; ++argv;
}
if (argc && strcmp(argv[0], "--toggle") == 0) {
--argc; ++argv;
toggle = true;
}
if (argc) {
char *mark = join_args(argv, argc);
// Remove all existing marks of this type
container_map(&root_container, find_marks_callback, mark);
if (view->marks) {
if (add) {
int index;
if ((index = list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1) {
if (toggle) {
free(view->marks->items[index]);
list_del(view->marks, index);
if (0 == view->marks->length) {
list_free(view->marks);
view->marks = NULL;
}
}
free(mark);
} else {
list_add(view->marks, mark);
}
} else {
if (toggle && list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark) != -1) {
// Delete the list
list_foreach(view->marks, free);
list_free(view->marks);
view->marks = NULL;
} else {
// Delete and replace with a new list
list_foreach(view->marks, free);
list_free(view->marks);
view->marks = create_list();
list_add(view->marks, mark);
}
}
} else {
view->marks = create_list();
list_add(view->marks, mark);
}
} else {
return cmd_results_new(CMD_FAILURE, "mark",
"Expected 'mark [--add|--replace] [--toggle] <mark>'");
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

View file

@ -20,7 +20,7 @@ struct cmd_results *cmd_move(int argc, char **argv) {
"'move <container|window> to workspace <name>' or " "'move <container|window> to workspace <name>' or "
"'move <container|window|workspace> to output <name|direction>' or " "'move <container|window|workspace> to output <name|direction>' or "
"'move position mouse'"; "'move position mouse'";
swayc_t *view = get_focused_container(&root_container); swayc_t *view = current_container;
if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0 )) { if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0 )) {
char *inv; char *inv;
@ -125,7 +125,7 @@ struct cmd_results *cmd_move(int argc, char **argv) {
if (view->type != C_CONTAINER && view->type != C_VIEW) { if (view->type != C_CONTAINER && view->type != C_VIEW) {
return cmd_results_new(CMD_FAILURE, "move scratchpad", "Can only move containers and views."); return cmd_results_new(CMD_FAILURE, "move scratchpad", "Can only move containers and views.");
} }
swayc_t *view = get_focused_container(&root_container); swayc_t *view = current_container;
int i; int i;
for (i = 0; i < scratchpad->length; i++) { for (i = 0; i < scratchpad->length; i++) {
if (scratchpad->items[i] == view) { if (scratchpad->items[i] == view) {

View file

@ -19,7 +19,7 @@ enum resize_dim_types {
}; };
static bool set_size_floating(int new_dimension, bool use_width) { static bool set_size_floating(int new_dimension, bool use_width) {
swayc_t *view = get_focused_float(swayc_active_workspace()); swayc_t *view = current_container;
if (view) { if (view) {
if (use_width) { if (use_width) {
int current_width = view->width; int current_width = view->width;
@ -50,7 +50,7 @@ static bool set_size_floating(int new_dimension, bool use_width) {
} }
static bool resize_floating(int amount, bool use_width) { static bool resize_floating(int amount, bool use_width) {
swayc_t *view = get_focused_float(swayc_active_workspace()); swayc_t *view = current_container;
if (view) { if (view) {
if (use_width) { if (use_width) {
@ -64,7 +64,7 @@ static bool resize_floating(int amount, bool use_width) {
} }
static bool resize_tiled(int amount, bool use_width) { static bool resize_tiled(int amount, bool use_width) {
swayc_t *container = get_focused_view(swayc_active_workspace()); swayc_t *container = current_container;
swayc_t *parent = container->parent; swayc_t *parent = container->parent;
int idx_focused = 0; int idx_focused = 0;
bool use_major = false; bool use_major = false;
@ -199,7 +199,7 @@ static bool resize_tiled(int amount, bool use_width) {
static bool set_size_tiled(int amount, bool use_width) { static bool set_size_tiled(int amount, bool use_width) {
int desired; int desired;
swayc_t *focused = get_focused_view(swayc_active_workspace()); swayc_t *focused = current_container;
if (use_width) { if (use_width) {
desired = amount - focused->width; desired = amount - focused->width;
@ -211,7 +211,7 @@ static bool set_size_tiled(int amount, bool use_width) {
} }
static bool set_size(int dimension, bool use_width) { static bool set_size(int dimension, bool use_width) {
swayc_t *focused = get_focused_view_include_floating(swayc_active_workspace()); swayc_t *focused = current_container;
if (focused) { if (focused) {
if (focused->is_floating) { if (focused->is_floating) {
@ -225,7 +225,7 @@ static bool set_size(int dimension, bool use_width) {
} }
static bool resize(int dimension, bool use_width, enum resize_dim_types dim_type) { static bool resize(int dimension, bool use_width, enum resize_dim_types dim_type) {
swayc_t *focused = get_focused_view_include_floating(swayc_active_workspace()); swayc_t *focused = current_container;
// translate "10 ppt" (10%) to appropriate # of pixels in case we need it // translate "10 ppt" (10%) to appropriate # of pixels in case we need it
float ppt_dim = (float)dimension / 100; float ppt_dim = (float)dimension / 100;

View file

@ -0,0 +1,13 @@
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
struct cmd_results *cmd_show_marks(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "show_marks", EXPECTED_EQUAL_TO, 1))) {
return error;
}
config->show_marks = !strcasecmp(argv[0], "on");
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

View file

@ -17,7 +17,7 @@ static struct cmd_results *_do_split(int argc, char **argv, int layout) {
if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) { if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) {
return error; return error;
} }
swayc_t *focused = get_focused_container(&root_container); swayc_t *focused = current_container;
// Case of floating window, don't split // Case of floating window, don't split
if (focused->is_floating) { if (focused->is_floating) {
@ -66,7 +66,7 @@ struct cmd_results *cmd_split(int argc, char **argv) {
} else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) { } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) {
_do_split(argc - 1, argv + 1, L_HORIZ); _do_split(argc - 1, argv + 1, L_HORIZ);
} else if (strcasecmp(argv[0], "t") == 0 || strcasecmp(argv[0], "toggle") == 0) { } else if (strcasecmp(argv[0], "t") == 0 || strcasecmp(argv[0], "toggle") == 0) {
swayc_t *focused = get_focused_container(&root_container); swayc_t *focused = current_container;
if (focused->parent->layout == L_VERT) { if (focused->parent->layout == L_VERT) {
_do_split(argc - 1, argv + 1, L_HORIZ); _do_split(argc - 1, argv + 1, L_HORIZ);
} else { } else {
@ -89,7 +89,7 @@ struct cmd_results *cmd_splith(int argc, char **argv) {
} }
struct cmd_results *cmd_splitt(int argc, char **argv) { struct cmd_results *cmd_splitt(int argc, char **argv) {
swayc_t *focused = get_focused_container(&root_container); swayc_t *focused = current_container;
if (focused->parent->layout == L_VERT) { if (focused->parent->layout == L_VERT) {
return _do_split(argc, argv, L_HORIZ); return _do_split(argc, argv, L_HORIZ);
} else { } else {

31
sway/commands/unmark.c Normal file
View file

@ -0,0 +1,31 @@
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
#include "list.h"
#include "stringop.h"
struct cmd_results *cmd_unmark(int argc, char **argv) {
swayc_t *view = current_container;
if (view->marks) {
if (argc) {
char *mark = join_args(argv, argc);
int index;
if ((index = list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1) {
free(view->marks->items[index]);
list_del(view->marks, index);
if (view->marks->length == 0) {
list_free(view->marks);
view->marks = NULL;
}
}
free(mark);
} else {
list_foreach(view->marks, free);
list_free(view->marks);
view->marks = NULL;
}
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

View file

@ -329,6 +329,7 @@ static void config_defaults(struct sway_config *config) {
config->auto_back_and_forth = false; config->auto_back_and_forth = false;
config->seamless_mouse = true; config->seamless_mouse = true;
config->reading = false; config->reading = false;
config->show_marks = true;
config->edge_gaps = true; config->edge_gaps = true;
config->smart_gaps = false; config->smart_gaps = false;

View file

@ -61,6 +61,10 @@ static void free_swayc(swayc_t *cont) {
} }
list_free(cont->floating); list_free(cont->floating);
} }
if (cont->marks) {
list_foreach(cont->marks, free);
list_free(cont->marks);
}
if (cont->parent) { if (cont->parent) {
remove_child(cont); remove_child(cont);
} }

View file

@ -2,7 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <regex.h> #include <pcre.h>
#include "sway/criteria.h" #include "sway/criteria.h"
#include "sway/container.h" #include "sway/container.h"
#include "sway/config.h" #include "sway/config.h"
@ -12,6 +12,7 @@
enum criteria_type { // *must* keep in sync with criteria_strings[] enum criteria_type { // *must* keep in sync with criteria_strings[]
CRIT_CLASS, CRIT_CLASS,
CRIT_CON_MARK,
CRIT_ID, CRIT_ID,
CRIT_INSTANCE, CRIT_INSTANCE,
CRIT_TITLE, CRIT_TITLE,
@ -22,16 +23,16 @@ enum criteria_type { // *must* keep in sync with criteria_strings[]
CRIT_LAST CRIT_LAST
}; };
// this *must* match the ordering in criteria_type enum static const char * const criteria_strings[CRIT_LAST] = {
static const char * const criteria_strings[] = { [CRIT_CLASS] = "class",
"class", [CRIT_CON_MARK] = "con_mark",
"id", [CRIT_ID] = "id",
"instance", [CRIT_INSTANCE] = "instance",
"title", [CRIT_TITLE] = "title",
"urgent", // either "latest" or "oldest" ... [CRIT_URGENT] = "urgent", // either "latest" or "oldest" ...
"window_role", [CRIT_WINDOW_ROLE] = "window_role",
"window_type", [CRIT_WINDOW_TYPE] = "window_type",
"workspace" [CRIT_WORKSPACE] = "workspace"
}; };
/** /**
@ -40,18 +41,13 @@ static const char * const criteria_strings[] = {
*/ */
struct crit_token { struct crit_token {
enum criteria_type type; enum criteria_type type;
regex_t *regex; pcre *regex;
char *raw; char *raw;
}; };
static void free_crit_token(struct crit_token *crit) { static void free_crit_token(struct crit_token *crit) {
if (crit->regex) { pcre_free(crit->regex);
regfree(crit->regex); free(crit->raw);
free(crit->regex);
}
if (crit->raw) {
free(crit->raw);
}
free(crit); free(crit);
} }
@ -188,18 +184,17 @@ static char *parse_criteria_name(enum criteria_type *type, char *name) {
} }
// Returns error string on failure or NULL otherwise. // Returns error string on failure or NULL otherwise.
static char *generate_regex(regex_t **regex, char *value) { static char *generate_regex(pcre **regex, char *value) {
*regex = calloc(1, sizeof(regex_t)); const char *reg_err;
int err = regcomp(*regex, value, REG_NOSUB); int offset;
if (err != 0) {
char *reg_err = malloc(64);
regerror(err, *regex, reg_err, 64);
*regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, &reg_err, &offset, NULL);
if (!*regex) {
const char *fmt = "Regex compilation (for '%s') failed: %s"; const char *fmt = "Regex compilation (for '%s') failed: %s";
int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3;
char *error = malloc(len); char *error = malloc(len);
snprintf(error, len, fmt, value, reg_err); snprintf(error, len, fmt, value, reg_err);
free(reg_err);
return error; return error;
} }
return NULL; return NULL;
@ -243,6 +238,10 @@ ect_cleanup:
return error; return error;
} }
static int regex_cmp(const char *item, const pcre *regex) {
return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0);
}
// test a single view if it matches list of criteria tokens (all of them). // test a single view if it matches list of criteria tokens (all of them).
static bool criteria_test(swayc_t *cont, list_t *tokens) { static bool criteria_test(swayc_t *cont, list_t *tokens) {
if (cont->type != C_VIEW) { if (cont->type != C_VIEW) {
@ -260,26 +259,34 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
if (focused->class && strcmp(cont->class, focused->class) == 0) { if (focused->class && strcmp(cont->class, focused->class) == 0) {
matches++; matches++;
} }
} else if (crit->regex && regexec(crit->regex, cont->class, 0, NULL, 0) == 0) { } else if (crit->regex && regex_cmp(cont->class, crit->regex) == 0) {
matches++; matches++;
} }
break; break;
case CRIT_CON_MARK:
if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) {
// Make sure it isn't matching the NUL string
if ((strcmp(crit->raw, "") == 0) == (list_seq_find(cont->marks, (int (*)(const void *, const void *))strcmp, "") != -1)) {
++matches;
}
}
break;
case CRIT_ID: case CRIT_ID:
if (!cont->app_id) { if (!cont->app_id) {
// ignore // ignore
} else if (crit->regex && regexec(crit->regex, cont->app_id, 0, NULL, 0) == 0) { } else if (crit->regex && regex_cmp(cont->app_id, crit->regex) == 0) {
matches++; matches++;
} }
break; break;
case CRIT_INSTANCE: case CRIT_INSTANCE:
if (!cont->instance) { if (!cont->instance) {
// ignore // ignore
} else if (strcmp(crit->raw, "focused") == 0) { } else if (crit_is_focused(crit->raw)) {
swayc_t *focused = get_focused_view(&root_container); swayc_t *focused = get_focused_view(&root_container);
if (focused->instance && strcmp(cont->instance, focused->instance) == 0) { if (focused->instance && strcmp(cont->instance, focused->instance) == 0) {
matches++; matches++;
} }
} else if (crit->regex && regexec(crit->regex, cont->instance, 0, NULL, 0) == 0) { } else if (crit->regex && regex_cmp(cont->instance, crit->regex) == 0) {
matches++; matches++;
} }
break; break;
@ -291,7 +298,7 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
if (focused->name && strcmp(cont->name, focused->name) == 0) { if (focused->name && strcmp(cont->name, focused->name) == 0) {
matches++; matches++;
} }
} else if (crit->regex && regexec(crit->regex, cont->name, 0, NULL, 0) == 0) { } else if (crit->regex && regex_cmp(cont->name, crit->regex) == 0) {
matches++; matches++;
} }
break; break;
@ -311,7 +318,7 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
if (focused_ws->name && strcmp(cont_ws->name, focused_ws->name) == 0) { if (focused_ws->name && strcmp(cont_ws->name, focused_ws->name) == 0) {
matches++; matches++;
} }
} else if (crit->regex && regexec(crit->regex, cont_ws->name, 0, NULL, 0) == 0) { } else if (crit->regex && regex_cmp(cont_ws->name, crit->regex) == 0) {
matches++; matches++;
} }
break; break;
@ -362,3 +369,21 @@ list_t *criteria_for(swayc_t *cont) {
} }
return matches; return matches;
} }
struct list_tokens {
list_t *list;
list_t *tokens;
};
static void container_match_add(swayc_t *container, struct list_tokens *list_tokens) {
if (criteria_test(container, list_tokens->tokens)) {
list_add(list_tokens->list, container);
}
}
list_t *container_for(list_t *tokens) {
struct list_tokens list_tokens = (struct list_tokens){create_list(), tokens};
container_map(&root_container, (void (*)(swayc_t *, void *))container_match_add, &list_tokens);
return list_tokens.list;
}

View file

@ -63,6 +63,7 @@ void ipc_client_handle_command(struct ipc_client *client);
bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length);
void ipc_get_workspaces_callback(swayc_t *workspace, void *data); void ipc_get_workspaces_callback(swayc_t *workspace, void *data);
void ipc_get_outputs_callback(swayc_t *container, void *data); void ipc_get_outputs_callback(swayc_t *container, void *data);
static void ipc_get_marks_callback(swayc_t *container, void *data);
void ipc_init(void) { void ipc_init(void) {
ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
@ -161,7 +162,8 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {
} }
int flags; int flags;
if ((flags=fcntl(client_fd, F_GETFD)) == -1 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { if ((flags = fcntl(client_fd, F_GETFD)) == -1
|| fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) {
sway_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket"); sway_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket");
close(client_fd); close(client_fd);
return 0; return 0;
@ -193,13 +195,12 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
if (mask & WLC_EVENT_ERROR) { if (mask & WLC_EVENT_ERROR) {
sway_log(L_ERROR, "IPC Client socket error, removing client"); sway_log(L_ERROR, "IPC Client socket error, removing client");
client->fd = -1;
ipc_client_disconnect(client); ipc_client_disconnect(client);
return 0; return 0;
} }
if (mask & WLC_EVENT_HANGUP) { if (mask & WLC_EVENT_HANGUP) {
client->fd = -1; sway_log(L_DEBUG, "Client %d hung up", client->fd);
ipc_client_disconnect(client); ipc_client_disconnect(client);
return 0; return 0;
} }
@ -456,6 +457,19 @@ void ipc_client_handle_command(struct ipc_client *client) {
goto exit_cleanup; goto exit_cleanup;
} }
case IPC_GET_MARKS:
{
if (!(client->security_policy & IPC_FEATURE_GET_MARKS)) {
goto exit_denied;
}
json_object *marks = json_object_new_array();
container_map(&root_container, ipc_get_marks_callback, marks);
const char *json_string = json_object_to_json_string(marks);
ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
json_object_put(marks);
goto exit_cleanup;
}
case IPC_GET_VERSION: case IPC_GET_VERSION:
{ {
json_object *version = ipc_json_get_version(); json_object *version = ipc_json_get_version();
@ -609,6 +623,16 @@ void ipc_get_outputs_callback(swayc_t *container, void *data) {
} }
} }
static void ipc_get_marks_callback(swayc_t *container, void *data) {
json_object *object = (json_object *)data;
if (container->marks) {
for (int i = 0; i < container->marks->length; ++i) {
char *mark = (char *)container->marks->items[i];
json_object_array_add(object, json_object_new_string(mark));
}
}
}
void ipc_send_event(const char *json_string, enum ipc_command_type event) { void ipc_send_event(const char *json_string, enum ipc_command_type event) {
static struct { static struct {
enum ipc_command_type event; enum ipc_command_type event;

View file

@ -11,8 +11,8 @@
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <getopt.h> #include <getopt.h>
#include <sys/capability.h>
#ifdef __linux__ #ifdef __linux__
#include <sys/capability.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#endif #endif
#include "sway/extensions.h" #include "sway/extensions.h"

View file

@ -48,8 +48,8 @@ providing such a great piece of software, so good that your users would rather
write an entirely new window manager from scratch that behaved _exactly_ like i3 write an entirely new window manager from scratch that behaved _exactly_ like i3
rather than switch to something else. rather than switch to something else.
You may run sway from an ongoing x11 session to run it within x. Otherwise, you Launch sway directly from a tty or via your favorite Wayland-compatible login
can run sway on a tty and it will use your outputs directly. manager.
*Important note for nvidia users*: The proprietary nvidia driver does _not_ have *Important note for nvidia users*: The proprietary nvidia driver does _not_ have
support for Wayland as of 2016-06-10. Use nouveau. support for Wayland as of 2016-06-10. Use nouveau.

View file

@ -316,6 +316,14 @@ The default colors are:
If smart_gaps are _on_ then gaps will only be enabled if a workspace has more If smart_gaps are _on_ then gaps will only be enabled if a workspace has more
than one child container. than one child container.
**mark** \<--add|--replace> \<--toggle> <identifier>::
Marks are arbitrary labels that can be used to identify certain windows and
then jump to them at a later time. By default, the **mark** command sets
_identifier_ as the only mark on a window. By specifying _--add_, mark will
add _identifier_ to the list of current marks. If _--toggle_ is specified mark
will remove _identifier_ if it is already a label. Marks may be found by using
a criteria. See the **Criteria** section below.
**mode** <mode_name>:: **mode** <mode_name>::
Switches to the given mode_name. The default mode is simply _default_. To Switches to the given mode_name. The default mode is simply _default_. To
create a new mode in config append _{_ to this command, the following lines create a new mode in config append _{_ to this command, the following lines
@ -368,6 +376,15 @@ The default colors are:
be configured with perfectly aligned adjacent positions for this option to be configured with perfectly aligned adjacent positions for this option to
have any effect. have any effect.
**show_marks** <on|off>::
If **show_marks** is on then marks will be showed in the window decoration.
However, any mark that starts with an underscore will not be drawn even if the
option is on. The default option is _on_.
**unmark** <identifier>::
**Unmark** will remove _identifier_ from the list of current marks on a window. If
no _identifier_ is specified then **unmark** will remove all marks.
**workspace** [number] <name>:: **workspace** [number] <name>::
Switches to the specified workspace. The string "number" is optional. The Switches to the specified workspace. The string "number" is optional. The
worspace _name_, if unquoted, may not contain the string "output", as sway worspace _name_, if unquoted, may not contain the string "output", as sway
@ -409,6 +426,20 @@ The string contains one or more (space separated) attribute/value pairs and they
are used by some commands filter which views to execute actions on. All attributes are used by some commands filter which views to execute actions on. All attributes
must match for the criteria string to match. must match for the criteria string to match.
Criteria may be used with either the **for_window** or **assign** commands to
specify operations to perform on new views. A criteria may also be used to
perform specific commands (ones that normally act upon one window) on all views
that match that criteria. For example:
Focus on a window with the mark "IRC":
[con_mark="IRC"] focus
Kill all windows with the title "Emacs":
[class="Emacs"] kill
Mark all Firefox windows with "Browser":
[class="Firefox"] mark Browser
Currently supported attributes: Currently supported attributes:
**class**:: **class**::
@ -416,6 +447,9 @@ Currently supported attributes:
is _focused_ then the window class must be the same as that of the currently is _focused_ then the window class must be the same as that of the currently
focused window. focused window.
**con_mark**::
Compare against the window marks. Can be a regular expression.
**id**:: **id**::
Compare value against the app id. Can be a regular expression. Compare value against the app id. Can be a regular expression.