Copy files over
This commit is contained in:
commit
9499f8bf03
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
((nil . ((tab-width . 4)
|
||||||
|
(c-basic-offset . 4)
|
||||||
|
(sentence-end-double-space . t)
|
||||||
|
(indent-tabs-mode . t)
|
||||||
|
(fill-column . 80)))
|
||||||
|
(c-mode . ((c-file-style . "GNU")
|
||||||
|
(c-noise-macro-names . ("UNINIT" "CALLBACK" "ALIGN_STACK")))))
|
|
@ -0,0 +1,29 @@
|
||||||
|
MIT/X Consortium License
|
||||||
|
|
||||||
|
© 2018-2019 Henrik Nyman <h@nyymanni.com>
|
||||||
|
© 2012 Steve Engledow <steve@offend.me.uk>
|
||||||
|
© 2010 Connor Lane Smith <cls@lubutu.com>
|
||||||
|
© 2006-2010 Anselm R Garbe <anselm@garbe.us>
|
||||||
|
© 2009 Gottox <gottox@s01.de>
|
||||||
|
© 2009 Markus Schnalke <meillo@marmaro.de>
|
||||||
|
© 2009 Evan Gates <evan.gates@gmail.com>
|
||||||
|
© 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com>
|
||||||
|
© 2006-2007 Michał Janeczek <janeczek at gmail dot com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,83 @@
|
||||||
|
# dmenu-wl - dynamic menu
|
||||||
|
dmenu-wl is an efficient dynamic menu for wayland (wlroots).
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
Missing dmenu (for X) features:
|
||||||
|
- Echo (non-interactive use)
|
||||||
|
- Handling pagination
|
||||||
|
- Vertical layout
|
||||||
|
- Return-early
|
||||||
|
- Control-Enter handling
|
||||||
|
- Keyrepeat
|
||||||
|
- Choice (-c) -mode
|
||||||
|
|
||||||
|
Other TODO items:
|
||||||
|
- Cleaner exiting
|
||||||
|
- Generate protocol files with `wayland-scanner`
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
Requires a compositor which implements wlr-layer-shell and xdg-output
|
||||||
|
protocols. Basically this means a wlroots-based compositor is needed.
|
||||||
|
Gnome and KDE are therefore [not supported](https://github.com/nyyManni/dmenu-wayland/issues/16).
|
||||||
|
Tested with sway 1.0.
|
||||||
|
|
||||||
|
Required libraries (and headers):
|
||||||
|
- wayland-client
|
||||||
|
- cairo
|
||||||
|
- pango-1.0
|
||||||
|
- pangocairo-1.0
|
||||||
|
- xkbcommon
|
||||||
|
- glib-2.0
|
||||||
|
- gobject-2.0
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
meson build
|
||||||
|
ninja -C build
|
||||||
|
sudo ninja -C build install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running dmenu-wl ...
|
||||||
|
|
||||||
|
### ... as the application launcher in Sway
|
||||||
|
|
||||||
|
Add to sway configuration (`~/.config/sway/config`) to run the launcher on Win+D.
|
||||||
|
|
||||||
|
bindsym $mod+d exec dmenu-wl_run -i
|
||||||
|
|
||||||
|
### ... from the command-line
|
||||||
|
|
||||||
|
See the man page for details.
|
||||||
|
```
|
||||||
|
Usage: dmenu-wl [OPTION]...
|
||||||
|
|
||||||
|
Display newline-separated input stdin as a menubar
|
||||||
|
|
||||||
|
-e, --echo display text from stdin with no user
|
||||||
|
interaction
|
||||||
|
-ec, --echo-centre same as -e but align text centrally
|
||||||
|
-er, --echo-right same as -e but align text right
|
||||||
|
-et, --echo-timeout SECS close the message after SEC seconds
|
||||||
|
when using -e, -ec, or -er
|
||||||
|
-b, --bottom dmenu appears at the bottom of the screen
|
||||||
|
-h, --height N set dmenu to be N pixels high
|
||||||
|
-i, --insensitive dmenu matches menu items case insensitively
|
||||||
|
-l, --lines LINES dmenu lists items vertically, within the
|
||||||
|
given number of lines
|
||||||
|
-m, --monitor MONITOR dmenu appears on the given monitor
|
||||||
|
(0-based index or monitor name)
|
||||||
|
-p, --prompt PROMPT prompt to be displayed to the left of the
|
||||||
|
input field
|
||||||
|
-po, --prompt-only PROMPT same as -p but don't wait for stdin
|
||||||
|
useful for a prompt with no menu
|
||||||
|
-r, --return-early return as soon as a single match is found
|
||||||
|
-fn, --font-name FONT font or font set to be used
|
||||||
|
-nb, --normal-background COLOR normal background color
|
||||||
|
#RRGGBB and #RRGGBBAA supported
|
||||||
|
-nf, --normal-foreground COLOR normal foreground color
|
||||||
|
-sb, --selected-background COLOR selected background color
|
||||||
|
-sf, --selected-foreground COLOR selected foreground color
|
||||||
|
-v, --version display version information
|
||||||
|
```
|
|
@ -0,0 +1,125 @@
|
||||||
|
.TH DMENU 1 dmenu-wl\-VERSION
|
||||||
|
.SH NAME
|
||||||
|
dmenu-wl \- dynamic menu
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B dmenu-wl
|
||||||
|
.RB [ \-b ]
|
||||||
|
.RB [ \-i ]
|
||||||
|
.RB [ \-l
|
||||||
|
.IR lines ]
|
||||||
|
.RB [ \-m
|
||||||
|
.IR monitor ]
|
||||||
|
.RB [ \-p
|
||||||
|
.IR prompt ]
|
||||||
|
.RB [ \-fn
|
||||||
|
.IR font ]
|
||||||
|
.RB [ \-nb
|
||||||
|
.IR color ]
|
||||||
|
.RB [ \-nf
|
||||||
|
.IR color ]
|
||||||
|
.RB [ \-sb
|
||||||
|
.IR color ]
|
||||||
|
.RB [ \-sf
|
||||||
|
.IR color ]
|
||||||
|
.RB [ \-v ]
|
||||||
|
.P
|
||||||
|
.BR dmenu-wl_run " ..."
|
||||||
|
.P
|
||||||
|
.B dmenu-wl_path
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B dmenu-wl
|
||||||
|
is a dynamic menu for Wayland, originally designed for X and
|
||||||
|
.BR dwm (1).
|
||||||
|
It manages huge numbers of user-defined menu items efficiently.
|
||||||
|
.P
|
||||||
|
dmenu-wl reads a list of newline-separated items from standard input and creates a
|
||||||
|
menu (or displays a notification if using -e). When the user selects an item or
|
||||||
|
enters any text and presses Return, their choice is printed to standard output
|
||||||
|
and dmenu-wl terminates.
|
||||||
|
.P
|
||||||
|
.B dmenu-wl_run
|
||||||
|
is a dmenu-wl script used by dwm which lists programs in the user's PATH and
|
||||||
|
executes the selected item.
|
||||||
|
.P
|
||||||
|
.B dmenu-wl_path
|
||||||
|
is a program used by dmenu-wl_run to find and cache a list of executables.
|
||||||
|
.SH OPTIONS
|
||||||
|
.B \-e
|
||||||
|
dmenu displays text from stdin with no user interaction.
|
||||||
|
.TP
|
||||||
|
.B \-ec
|
||||||
|
same as -e but text is aligned centrally.
|
||||||
|
.TP
|
||||||
|
.B \-er
|
||||||
|
same as -e but text is aligned to the right.
|
||||||
|
.TP
|
||||||
|
.BI \-et " secs "
|
||||||
|
when using -e, close the message after the given number of seconds
|
||||||
|
.TP
|
||||||
|
.B \-b
|
||||||
|
dmenu appears at the bottom of the screen.
|
||||||
|
.TP
|
||||||
|
.BI \-h " height"
|
||||||
|
dmenu is drawn at least the given number of pixels high.
|
||||||
|
.TP
|
||||||
|
.B \-i
|
||||||
|
dmenu matches menu items case insensitively.
|
||||||
|
.TP
|
||||||
|
.BI \-l " lines"
|
||||||
|
dmenu lists items vertically, within the given number of lines.
|
||||||
|
.TP
|
||||||
|
.BI \-m " monitor"
|
||||||
|
dmenu appears on the given monitor (0-based index or monitor name)
|
||||||
|
.TP
|
||||||
|
.BI \-p " prompt"
|
||||||
|
defines the prompt to be displayed to the left of the input field.
|
||||||
|
.TP
|
||||||
|
.BI \-po " prompt"
|
||||||
|
same as -p but don't wait for stdin - useful for a prompt with no selection options.
|
||||||
|
.TP
|
||||||
|
.B \-r
|
||||||
|
Return as soon as a single match is found. i.e. don't wait for the user to press return.
|
||||||
|
.TP
|
||||||
|
.BI \-fn " font"
|
||||||
|
defines the font or font set used.
|
||||||
|
.TP
|
||||||
|
.BI \-nb " color"
|
||||||
|
defines the normal background color.
|
||||||
|
.IR #RRGGBB and
|
||||||
|
.IR #RRGGBBAA
|
||||||
|
are supported.
|
||||||
|
.TP
|
||||||
|
.BI \-nf " color"
|
||||||
|
defines the normal foreground color.
|
||||||
|
.TP
|
||||||
|
.BI \-sb " color"
|
||||||
|
defines the selected background color.
|
||||||
|
.TP
|
||||||
|
.BI \-sf " color"
|
||||||
|
defines the selected foreground color.
|
||||||
|
.TP
|
||||||
|
.B \-v
|
||||||
|
prints version information to standard output, then exits.
|
||||||
|
.SH USAGE
|
||||||
|
dmenu-wl is completely controlled by the keyboard. Besides standard Unix line
|
||||||
|
editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the
|
||||||
|
following keys are recognized:
|
||||||
|
.TP
|
||||||
|
.B Tab (Control\-i)
|
||||||
|
Copy the selected item to the input field.
|
||||||
|
.TP
|
||||||
|
.B Return (Control\-j)
|
||||||
|
Confirm selection. Prints the selected item to standard output and exits,
|
||||||
|
returning success.
|
||||||
|
.TP
|
||||||
|
.B Shift\-Return (Control\-Shift\-j)
|
||||||
|
Confirm input. Prints the input text to standard output and exits, returning
|
||||||
|
success.
|
||||||
|
.TP
|
||||||
|
.B Escape (Control\-c)
|
||||||
|
Exit without selecting an item, returning failure.
|
||||||
|
.TP
|
||||||
|
.B Control\-y
|
||||||
|
Paste the current selection into the input field.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR dwm (1)
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
dmenu-wl_path | dmenu-wl "$@" | ${SHELL:-"/bin/sh"} &
|
|
@ -0,0 +1,612 @@
|
||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include "draw.h"
|
||||||
|
|
||||||
|
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
|
||||||
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||||
|
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||||
|
|
||||||
|
typedef struct Item Item;
|
||||||
|
struct Item {
|
||||||
|
char *text;
|
||||||
|
Item *next; /* traverses all items */
|
||||||
|
Item *left, *right; /* traverses matching items */
|
||||||
|
int32_t width;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LEFT,
|
||||||
|
RIGHT,
|
||||||
|
CENTRE
|
||||||
|
} TextPosition;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int32_t width;
|
||||||
|
int32_t height;
|
||||||
|
int32_t text_height;
|
||||||
|
int32_t text_y;
|
||||||
|
int32_t input_field;
|
||||||
|
int32_t scroll_left;
|
||||||
|
int32_t matches;
|
||||||
|
int32_t scroll_right;
|
||||||
|
} window_config;
|
||||||
|
|
||||||
|
const char *progname;
|
||||||
|
|
||||||
|
static uint32_t color_bg = 0x222222ff;
|
||||||
|
static uint32_t color_fg = 0xbbbbbbff;
|
||||||
|
static uint32_t color_input_bg = 0x222222ff;
|
||||||
|
static uint32_t color_input_fg = 0xbbbbbbff;
|
||||||
|
static uint32_t color_prompt_bg = 0x005577ff;
|
||||||
|
static uint32_t color_prompt_fg = 0xeeeeeeff;
|
||||||
|
static uint32_t color_selected_bg = 0x005577ff;
|
||||||
|
static uint32_t color_selected_fg = 0xeeeeeeff;
|
||||||
|
|
||||||
|
static int32_t panel_height = 20;
|
||||||
|
|
||||||
|
static void appenditem(Item *item, Item **list, Item **last);
|
||||||
|
static char *fstrstr(const char *s, const char *sub);
|
||||||
|
static void insert(const char *s, ssize_t n);
|
||||||
|
static void match(void);
|
||||||
|
static size_t nextrune(int incr);
|
||||||
|
static void readstdin(void);
|
||||||
|
static void alarmhandler(int signum);
|
||||||
|
/* static void handle_return(char* value); */
|
||||||
|
static void usage(void);
|
||||||
|
static int retcode = EXIT_SUCCESS;
|
||||||
|
static int selected_monitor = 0;
|
||||||
|
static char *selected_monitor_name = 0;
|
||||||
|
|
||||||
|
static char text[BUFSIZ];
|
||||||
|
static char text_[BUFSIZ];
|
||||||
|
static int itemcount = 0;
|
||||||
|
static int lines = 0;
|
||||||
|
static int timeout = 3;
|
||||||
|
static size_t cursor = 0;
|
||||||
|
static const char *prompt = NULL;
|
||||||
|
static bool message = false;
|
||||||
|
static bool nostdin = false;
|
||||||
|
static bool returnearly = false;
|
||||||
|
static bool show_in_bottom = false;
|
||||||
|
static TextPosition messageposition = LEFT;
|
||||||
|
static Item *items = NULL;
|
||||||
|
static Item *matches, *sel;
|
||||||
|
static Item *prev, *curr, *next;
|
||||||
|
static Item *leftmost, *rightmost;
|
||||||
|
static char *font = "Mono";
|
||||||
|
|
||||||
|
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
|
||||||
|
|
||||||
|
void
|
||||||
|
insert(const char *s, ssize_t n) {
|
||||||
|
if(strlen(text) + n > sizeof text - 1)
|
||||||
|
return;
|
||||||
|
memmove(text + cursor + n, text + cursor, sizeof text - cursor - MAX(n, 0));
|
||||||
|
if(n > 0)
|
||||||
|
memcpy(text + cursor, s, n);
|
||||||
|
cursor += n;
|
||||||
|
match();
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyrepeat(struct dmenu_panel *panel) {
|
||||||
|
if (panel->on_keyevent) {
|
||||||
|
panel->on_keyevent(panel, panel->repeat_key_state, panel->repeat_sym,
|
||||||
|
panel->keyboard.control, panel->keyboard.shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void keypress(struct dmenu_panel *panel, enum wl_keyboard_key_state state,
|
||||||
|
xkb_keysym_t sym, bool ctrl, bool shft) {
|
||||||
|
char buf[8];
|
||||||
|
size_t len = strlen(text);
|
||||||
|
|
||||||
|
if (state != WL_KEYBOARD_KEY_STATE_PRESSED) return;
|
||||||
|
|
||||||
|
if (ctrl) {
|
||||||
|
switch (xkb_keysym_to_lower(sym)) {
|
||||||
|
case XKB_KEY_a:
|
||||||
|
sym = XKB_KEY_Home;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_e:
|
||||||
|
sym = XKB_KEY_End;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_f:
|
||||||
|
case XKB_KEY_n:
|
||||||
|
sym = XKB_KEY_Right;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_b:
|
||||||
|
case XKB_KEY_p:
|
||||||
|
sym = XKB_KEY_Left;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_h:
|
||||||
|
sym = XKB_KEY_BackSpace;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_j:
|
||||||
|
sym = XKB_KEY_Return;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_g:
|
||||||
|
case XKB_KEY_c:
|
||||||
|
retcode = EXIT_FAILURE;
|
||||||
|
dmenu_close(panel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (sym) {
|
||||||
|
case XKB_KEY_KP_Enter: /* fallthrough */
|
||||||
|
case XKB_KEY_Return:
|
||||||
|
dmenu_close(panel);
|
||||||
|
fputs((sel && !shft) ? sel->text : text, stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Escape:
|
||||||
|
retcode = EXIT_FAILURE;
|
||||||
|
dmenu_close(panel);
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Left:
|
||||||
|
if(cursor && (!sel || !sel->left)) {
|
||||||
|
cursor = nextrune(-1);
|
||||||
|
} if (sel && sel->left) {
|
||||||
|
sel = sel->left;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Right:
|
||||||
|
if (cursor < len) {
|
||||||
|
cursor = nextrune(+1);
|
||||||
|
} else if (cursor == len) {
|
||||||
|
if (sel && sel->right) sel = sel->right;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XKB_KEY_End:
|
||||||
|
if(cursor < len) {
|
||||||
|
cursor = len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while(sel && sel->right)
|
||||||
|
sel = sel->right;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Home:
|
||||||
|
if(sel == matches) {
|
||||||
|
cursor = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sel = curr = matches;
|
||||||
|
/* calcoffsets(); */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XKB_KEY_BackSpace:
|
||||||
|
if (cursor > 0)
|
||||||
|
insert(NULL, nextrune(-1) - cursor);
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Delete:
|
||||||
|
if (cursor == len)
|
||||||
|
return;
|
||||||
|
cursor = nextrune(+1);
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Tab:
|
||||||
|
if(!sel) return;
|
||||||
|
strncpy(text, sel->text, sizeof text);
|
||||||
|
cursor = strlen(text);
|
||||||
|
match();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (xkb_keysym_to_utf8(sym, buf, 8)) {
|
||||||
|
insert(buf, strnlen(buf, 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dmenu_draw(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t draw_text(cairo_t *cairo, int32_t width, int32_t height, const char *str,
|
||||||
|
int32_t x, int32_t scale, uint32_t
|
||||||
|
foreground_color, uint32_t background_color, int32_t padding) {
|
||||||
|
|
||||||
|
int32_t text_width, text_height;
|
||||||
|
get_text_size(cairo, font, &text_width, &text_height,
|
||||||
|
NULL, scale, false, str);
|
||||||
|
int32_t text_y = (height / 2.0) - (text_height / 2.0);
|
||||||
|
|
||||||
|
if (x + padding * scale + text_width + 30 * scale > width) {
|
||||||
|
/* if (x + padding * scale + text_width > width) { */
|
||||||
|
|
||||||
|
cairo_move_to(cairo, width, text_y);
|
||||||
|
pango_printf(cairo, font, scale, false, ">");
|
||||||
|
} else {
|
||||||
|
if (background_color) {
|
||||||
|
cairo_set_source_u32(cairo, background_color);
|
||||||
|
cairo_rectangle(cairo, x, 0, text_width + 2 * padding * scale, height);
|
||||||
|
cairo_fill(cairo);
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_move_to(cairo, x + padding * scale, text_y);
|
||||||
|
cairo_set_source_u32(cairo, foreground_color);
|
||||||
|
|
||||||
|
pango_printf(cairo, font, scale, false, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return x + text_width + 2 * padding * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(cairo_t *cairo, int32_t width, int32_t height, int32_t scale) {
|
||||||
|
|
||||||
|
int32_t x = window_config.input_field;
|
||||||
|
|
||||||
|
int32_t item_padding = 10;
|
||||||
|
|
||||||
|
int32_t text_width, text_height;
|
||||||
|
get_text_size(cairo, font, &text_width, &text_height, NULL, scale,
|
||||||
|
false, "Aj");
|
||||||
|
int32_t text_y = (height / 2.0) - (text_height / 2.0);
|
||||||
|
|
||||||
|
cairo_set_source_u32(cairo, color_bg);
|
||||||
|
cairo_paint(cairo);
|
||||||
|
|
||||||
|
if (prompt) {
|
||||||
|
x = draw_text(cairo, width, height, prompt, 0, scale, color_prompt_fg,
|
||||||
|
color_prompt_bg, 6);
|
||||||
|
window_config.input_field = x;
|
||||||
|
} else {
|
||||||
|
window_config.input_field = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_set_source_u32(cairo, color_input_bg);
|
||||||
|
cairo_rectangle(cairo, window_config.input_field, 0, 300 * scale, height);
|
||||||
|
cairo_fill(cairo);
|
||||||
|
|
||||||
|
draw_text(cairo, width, height, text, x, scale, color_input_fg, 0, 6);
|
||||||
|
|
||||||
|
{
|
||||||
|
/* draw cursor */
|
||||||
|
memset(text_, 0, BUFSIZ);
|
||||||
|
strncpy(text_, text, cursor);
|
||||||
|
int32_t text_width, text_height;
|
||||||
|
get_text_size(cairo, font, &text_width, &text_height, NULL, scale,
|
||||||
|
false, text_);
|
||||||
|
/* int32_t text_y = (height / 2.0) - (text_height / 2.0); */
|
||||||
|
int32_t padding = 6 * scale;
|
||||||
|
cairo_rectangle(cairo, x + padding + text_width, text_y,
|
||||||
|
scale, text_height);
|
||||||
|
cairo_fill(cairo);
|
||||||
|
}
|
||||||
|
|
||||||
|
x += 300 * scale;
|
||||||
|
|
||||||
|
/* Scroll indicator will be drawn later if required. */
|
||||||
|
int32_t scroll_indicator_pos = x;
|
||||||
|
x += 20 * scale;
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
/* draw matches */
|
||||||
|
Item *item;
|
||||||
|
/* for (item = matches; item; item = item->right) { */
|
||||||
|
/* if (item->width == -1) { */
|
||||||
|
/* get_text_size(cairo, font, &item->width, NULL, NULL, scale, */
|
||||||
|
/* false, item->text); */
|
||||||
|
/* item->width += item_padding; */
|
||||||
|
/* /\* printf("%d ", item->width); *\/ */
|
||||||
|
/* } */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
/* /\* Figure out if we need to scroll. *\/ */
|
||||||
|
/* int32_t item_pos = x; */
|
||||||
|
/* bool found = false; */
|
||||||
|
/* rightmost = NULL; */
|
||||||
|
/* for (item = leftmost; item; item = item->right) { */
|
||||||
|
/* item_pos += item->width; */
|
||||||
|
/* if (item_pos >= (width - x - 80 * scale)) { */
|
||||||
|
/* rightmost = item->left; */
|
||||||
|
/* printf("rightmost: %s\n", item->left->text); */
|
||||||
|
/* found = true; */
|
||||||
|
/* break; */
|
||||||
|
/* } */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
for (item = matches; item; item = item->right) {
|
||||||
|
uint32_t bg_color = sel == item ? color_selected_bg : color_bg;
|
||||||
|
uint32_t fg_color = sel == item ? color_selected_fg : color_fg;
|
||||||
|
if (x < width) {
|
||||||
|
/* x = draw_text(cairo, width - 20 * scale, height, item->text, */
|
||||||
|
/* x, scale, fg_color, bg_color, item_padding); */
|
||||||
|
x = draw_text(cairo, width - 20 * scale, height, item->text,
|
||||||
|
x, scale, fg_color, bg_color, item_padding);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftmost != matches) {
|
||||||
|
cairo_move_to(cairo, scroll_indicator_pos, text_y);
|
||||||
|
pango_printf(cairo, font, scale, false, "<");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t parse_color(char *str) {
|
||||||
|
if (!str) eprintf("NULL as color value\n");
|
||||||
|
|
||||||
|
size_t len = strnlen(str, BUFSIZ);
|
||||||
|
|
||||||
|
if ((len != 7 && len != 9) || str[0] != '#')
|
||||||
|
eprintf("Color format must be '#rrggbb[aa]'\n");
|
||||||
|
|
||||||
|
uint32_t _val = strtol(&str[1], NULL, 16);
|
||||||
|
|
||||||
|
uint32_t color = 0x000000ff;
|
||||||
|
if (len == 9) /* Alpha specified */
|
||||||
|
color = _val;
|
||||||
|
else /* No alpha specified, assume full opacity */
|
||||||
|
color = (_val << 8) + 0xff;
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
progname = "dmenu";
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "-v") || !strcmp(argv[1], "--version")) {
|
||||||
|
fputs("dmenu-wl-" VERSION
|
||||||
|
", © 2006-2018 dmenu engineers, see LICENSE for details\n",
|
||||||
|
stdout);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
} else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--bottom"))
|
||||||
|
show_in_bottom = true;
|
||||||
|
else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--echo"))
|
||||||
|
message = true;
|
||||||
|
else if (!strcmp(argv[i], "-ec") || !strcmp(argv[i], "--echo-centre"))
|
||||||
|
message = true, messageposition = CENTRE;
|
||||||
|
else if (!strcmp(argv[i], "-er") || !strcmp(argv[i], "--echo-right"))
|
||||||
|
message = true, messageposition = RIGHT;
|
||||||
|
else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--insensitive"))
|
||||||
|
fstrncmp = strncasecmp;
|
||||||
|
else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--return-early"))
|
||||||
|
returnearly = true;
|
||||||
|
else if (i == argc - 1) {
|
||||||
|
printf("2\n");
|
||||||
|
usage();
|
||||||
|
|
||||||
|
}
|
||||||
|
/* opts that need 1 arg */
|
||||||
|
else if (!strcmp(argv[i], "-et") || !strcmp(argv[i], "--echo-timeout"))
|
||||||
|
timeout = atoi(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--height"))
|
||||||
|
panel_height = atoi(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--lines"))
|
||||||
|
lines = atoi(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--monitor")) {
|
||||||
|
++i;
|
||||||
|
bool is_num = true;
|
||||||
|
for (int j = 0; j < strlen(argv[i]); ++j) {
|
||||||
|
if (!isdigit(argv[i][j])) {
|
||||||
|
is_num = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_num) {
|
||||||
|
selected_monitor = atoi(argv[i]);
|
||||||
|
} else {
|
||||||
|
selected_monitor = -1;
|
||||||
|
selected_monitor_name = argv[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--prompt"))
|
||||||
|
prompt = argv[++i];
|
||||||
|
else if (!strcmp(argv[i], "-po") || !strcmp(argv[i], "--prompt-only"))
|
||||||
|
prompt = argv[++i], nostdin = true;
|
||||||
|
else if (!strcmp(argv[i], "-fn") || !strcmp(argv[i], "--font-name"))
|
||||||
|
font = argv[++i];
|
||||||
|
else if (!strcmp(argv[i], "-nb") || !strcmp(argv[i], "--normal-background"))
|
||||||
|
color_bg = color_input_bg = parse_color(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "-nf") || !strcmp(argv[i], "--normal-foreground"))
|
||||||
|
color_fg = color_input_fg = parse_color(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "-sb") ||
|
||||||
|
!strcmp(argv[i], "--selected-background"))
|
||||||
|
color_prompt_bg = color_selected_bg = parse_color(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "-sf") ||
|
||||||
|
!strcmp(argv[i], "--selected-foreground"))
|
||||||
|
color_prompt_fg = color_selected_fg = parse_color(argv[++i]);
|
||||||
|
else {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
signal(SIGALRM, alarmhandler);
|
||||||
|
alarm(timeout);
|
||||||
|
}
|
||||||
|
if(!nostdin) {
|
||||||
|
readstdin();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dmenu_panel dmenu;
|
||||||
|
dmenu.selected_monitor = selected_monitor;
|
||||||
|
dmenu.selected_monitor_name = selected_monitor_name;
|
||||||
|
dmenu_init_panel(&dmenu, panel_height, show_in_bottom);
|
||||||
|
|
||||||
|
|
||||||
|
dmenu.on_keyevent = keypress;
|
||||||
|
dmenu.on_keyrepeat = keyrepeat;
|
||||||
|
dmenu.draw = draw;
|
||||||
|
match();
|
||||||
|
|
||||||
|
struct monitor_info *monitor = dmenu.monitor;
|
||||||
|
|
||||||
|
double factor = monitor->scale / ((double)monitor->physical_width / monitor->logical_width);
|
||||||
|
|
||||||
|
window_config.height =round_to_int(dmenu.height / ((double)monitor->physical_width
|
||||||
|
/ monitor->logical_width));
|
||||||
|
window_config.height *= monitor->scale;
|
||||||
|
|
||||||
|
window_config.width = round_to_int(monitor->physical_width * factor);
|
||||||
|
get_text_size(dmenu.surface.cairo, font, NULL, &window_config.text_height,
|
||||||
|
NULL, monitor->scale, false, "Aj");
|
||||||
|
window_config.text_y = (window_config.height / 2.0) - (window_config.text_height / 2.0);
|
||||||
|
|
||||||
|
|
||||||
|
dmenu_show(&dmenu);
|
||||||
|
|
||||||
|
return retcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
appenditem(Item *item, Item **list, Item **last) {
|
||||||
|
if(!*last)
|
||||||
|
*list = item;
|
||||||
|
else
|
||||||
|
(*last)->right = item;
|
||||||
|
item->left = *last;
|
||||||
|
item->right = NULL;
|
||||||
|
*last = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
fstrstr(const char *s, const char *sub) {
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
for(len = strlen(sub); *s; s++)
|
||||||
|
if(!fstrncmp(s, sub, len))
|
||||||
|
return (char *)s;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
match(void) {
|
||||||
|
size_t len;
|
||||||
|
Item *item, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend;
|
||||||
|
|
||||||
|
rightmost = leftmost = NULL;
|
||||||
|
len = strlen(text);
|
||||||
|
matches = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL;
|
||||||
|
for(item = items; item; item = item->next)
|
||||||
|
if(!fstrncmp(text, item->text, len + 1)) {
|
||||||
|
appenditem(item, &lexact, &exactend);
|
||||||
|
}
|
||||||
|
else if(!fstrncmp(text, item->text, len)) {
|
||||||
|
appenditem(item, &lprefix, &prefixend);
|
||||||
|
}
|
||||||
|
else if(fstrstr(item->text, text)) {
|
||||||
|
appenditem(item, &lsubstr, &substrend);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lexact) {
|
||||||
|
matches = lexact;
|
||||||
|
itemend = exactend;
|
||||||
|
}
|
||||||
|
if(lprefix) {
|
||||||
|
if(itemend) {
|
||||||
|
itemend->right = lprefix;
|
||||||
|
lprefix->left = itemend;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
matches = lprefix;
|
||||||
|
itemend = prefixend;
|
||||||
|
}
|
||||||
|
if(lsubstr) {
|
||||||
|
if(itemend) {
|
||||||
|
itemend->right = lsubstr;
|
||||||
|
lsubstr->left = itemend;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
matches = lsubstr;
|
||||||
|
}
|
||||||
|
curr = prev = next = sel = matches;
|
||||||
|
/* calcoffsets(); */
|
||||||
|
|
||||||
|
leftmost = matches;
|
||||||
|
|
||||||
|
if(returnearly && !curr->right) {
|
||||||
|
/* handle_return(curr->text); */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
nextrune(int incr) {
|
||||||
|
size_t n, len;
|
||||||
|
|
||||||
|
len = strlen(text);
|
||||||
|
for(n = cursor + incr; n >= 0 && n < len && (text[n] & 0xc0) == 0x80; n += incr);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
readstdin(void) {
|
||||||
|
char buf[sizeof text], *p;
|
||||||
|
Item *item, **end;
|
||||||
|
|
||||||
|
for(end = &items; fgets(buf, sizeof buf, stdin); *end = item, end = &item->next) {
|
||||||
|
itemcount++;
|
||||||
|
|
||||||
|
if((p = strchr(buf, '\n'))) {
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
|
if(!(item = malloc(sizeof *item))) {
|
||||||
|
eprintf("cannot malloc %u bytes\n", sizeof *item);
|
||||||
|
}
|
||||||
|
item->width = -1;
|
||||||
|
if(!(item->text = strdup(buf))) {
|
||||||
|
eprintf("cannot strdup %u bytes\n", strlen(buf)+1);
|
||||||
|
}
|
||||||
|
item->next = item->left = item->right = NULL;
|
||||||
|
/* inputw = MAX(inputw, textw(dc, item->text)); */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
alarmhandler(int signum) {
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usage(void) {
|
||||||
|
printf("Usage: dmenu [OPTION]...\n");
|
||||||
|
printf("Display newline-separated input stdin as a menubar\n");
|
||||||
|
printf("\n");
|
||||||
|
printf(" -e, --echo display text from stdin with no user\n");
|
||||||
|
printf(" interaction\n");
|
||||||
|
printf(" -ec, --echo-centre same as -e but align text centrally\n");
|
||||||
|
printf(" -er, --echo-right same as -e but align text right\n");
|
||||||
|
printf(" -et, --echo-timeout SECS close the message after SEC seconds\n");
|
||||||
|
printf(" when using -e, -ec, or -er\n");
|
||||||
|
printf(" -b, --bottom dmenu appears at the bottom of the screen\n");
|
||||||
|
printf(" -h, --height N set dmenu to be N pixels high\n");
|
||||||
|
printf(" -i, --insensitive dmenu matches menu items case insensitively\n");
|
||||||
|
printf(" -l, --lines LINES dmenu lists items vertically, within the\n");
|
||||||
|
printf(" given number of lines\n");
|
||||||
|
printf(" -m, --monitor MONITOR dmenu appears on the given Xinerama screen\n");
|
||||||
|
printf(" (does nothing on wayland, supported for)\n");
|
||||||
|
printf(" compatibility with dmenu.\n");
|
||||||
|
printf(" -p, --prompt PROMPT prompt to be displayed to the left of the\n");
|
||||||
|
printf(" input field\n");
|
||||||
|
printf(" -po, --prompt-only PROMPT same as -p but don't wait for stdin\n");
|
||||||
|
printf(" useful for a prompt with no menu\n");
|
||||||
|
printf(" -r, --return-early return as soon as a single match is found\n");
|
||||||
|
printf(" -fn, --font-name FONT font or font set to be used\n");
|
||||||
|
printf(" -nb, --normal-background COLOR normal background color\n");
|
||||||
|
printf(" #RRGGBB and #RRGGBBAA supported\n");
|
||||||
|
printf(" -nf, --normal-foreground COLOR normal foreground color\n");
|
||||||
|
printf(" -sb, --selected-background COLOR selected background color\n");
|
||||||
|
printf(" -sf, --selected-foreground COLOR selected foreground color\n");
|
||||||
|
printf(" -v, --version display version information\n");
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#define CACHE ".dmenu_cache"
|
||||||
|
|
||||||
|
static void die(const char *s);
|
||||||
|
static int qstrcmp(const void *a, const void *b);
|
||||||
|
static void scan(void);
|
||||||
|
static int uptodate(void);
|
||||||
|
|
||||||
|
static char **items = NULL;
|
||||||
|
static const char *home, *path;
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void) {
|
||||||
|
if(!(home = getenv("HOME")))
|
||||||
|
die("no $HOME");
|
||||||
|
if(!(path = getenv("PATH")))
|
||||||
|
die("no $PATH");
|
||||||
|
if(chdir(home) < 0)
|
||||||
|
die("chdir failed");
|
||||||
|
if(uptodate()) {
|
||||||
|
execlp("cat", "cat", CACHE, NULL);
|
||||||
|
die("exec failed");
|
||||||
|
}
|
||||||
|
scan();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
die(const char *s) {
|
||||||
|
fprintf(stderr, "dmenu_path: %s\n", s);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
qstrcmp(const void *a, const void *b) {
|
||||||
|
return strcmp(*(const char **)a, *(const char **)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scan(void) {
|
||||||
|
char buf[PATH_MAX];
|
||||||
|
char *dir, *p;
|
||||||
|
size_t i, count;
|
||||||
|
struct dirent *ent;
|
||||||
|
DIR *dp;
|
||||||
|
FILE *cache;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
if(!(p = strdup(path)))
|
||||||
|
die("strdup failed");
|
||||||
|
for(dir = strtok(p, ":"); dir; dir = strtok(NULL, ":")) {
|
||||||
|
if(!(dp = opendir(dir)))
|
||||||
|
continue;
|
||||||
|
while((ent = readdir(dp))) {
|
||||||
|
snprintf(buf, sizeof buf, "%s/%s", dir, ent->d_name);
|
||||||
|
if(ent->d_name[0] == '.' || access(buf, X_OK) < 0)
|
||||||
|
continue;
|
||||||
|
if(!(items = realloc(items, ++count * sizeof *items)))
|
||||||
|
die("malloc failed");
|
||||||
|
if(!(items[count-1] = strdup(ent->d_name)))
|
||||||
|
die("strdup failed");
|
||||||
|
}
|
||||||
|
closedir(dp);
|
||||||
|
}
|
||||||
|
qsort(items, count, sizeof *items, qstrcmp);
|
||||||
|
if(!(cache = fopen(CACHE, "w")))
|
||||||
|
die("open failed");
|
||||||
|
for(i = 0; i < count; i++) {
|
||||||
|
if(i > 0 && !strcmp(items[i], items[i-1]))
|
||||||
|
continue;
|
||||||
|
fprintf(cache, "%s\n", items[i]);
|
||||||
|
fprintf(stdout, "%s\n", items[i]);
|
||||||
|
}
|
||||||
|
fclose(cache);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
uptodate(void) {
|
||||||
|
char *dir, *p;
|
||||||
|
time_t mtime;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if(stat(CACHE, &st) < 0)
|
||||||
|
return 0;
|
||||||
|
mtime = st.st_mtime;
|
||||||
|
if(!(p = strdup(path)))
|
||||||
|
die("strdup failed");
|
||||||
|
for(dir = strtok(p, ":"); dir; dir = strtok(NULL, ":"))
|
||||||
|
if(!stat(dir, &st) && st.st_mtime > mtime)
|
||||||
|
return 0;
|
||||||
|
free(p);
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,659 @@
|
||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <locale.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
|
#include <pango/pangocairo.h>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include "draw.h"
|
||||||
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const char overflow[] = "[buffer overflow]";
|
||||||
|
static const int max_chars = 16384;
|
||||||
|
|
||||||
|
struct monitor_info *monitors[16] = {0};
|
||||||
|
static int n_monitors = 0;
|
||||||
|
|
||||||
|
int32_t round_to_int(double val) {
|
||||||
|
return (int32_t)(val + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void randname(char *buf) {
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
long r = ts.tv_nsec;
|
||||||
|
for (int i = 0; i < 6; ++i) {
|
||||||
|
buf[i] = 'A'+(r&15)+(r&16)*2;
|
||||||
|
r >>= 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int anonymous_shm_open(void) {
|
||||||
|
char name[] = "/dmenu-XXXXXX";
|
||||||
|
int retries = 100;
|
||||||
|
|
||||||
|
do {
|
||||||
|
randname(name + strlen(name) - 6);
|
||||||
|
|
||||||
|
--retries;
|
||||||
|
// shm_open guarantees that O_CLOEXEC is set
|
||||||
|
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||||
|
if (fd >= 0) {
|
||||||
|
shm_unlink(name);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
} while (retries > 0 && errno == EEXIST);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_shm_file(off_t size) {
|
||||||
|
int fd = anonymous_shm_open();
|
||||||
|
if (fd < 0) {
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftruncate(fd, size) < 0) {
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
|
||||||
|
const char *text, double scale, bool markup) {
|
||||||
|
PangoLayout *layout = pango_cairo_create_layout(cairo);
|
||||||
|
PangoAttrList *attrs;
|
||||||
|
if (markup) {
|
||||||
|
char *buf;
|
||||||
|
GError *error = NULL;
|
||||||
|
if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) {
|
||||||
|
pango_layout_set_text(layout, buf, -1);
|
||||||
|
free(buf);
|
||||||
|
} else {
|
||||||
|
/* wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", text, */
|
||||||
|
/* error->message); */
|
||||||
|
g_error_free(error);
|
||||||
|
markup = false; // fallback to plain text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!markup) {
|
||||||
|
attrs = pango_attr_list_new();
|
||||||
|
pango_layout_set_text(layout, text, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
|
||||||
|
PangoFontDescription *desc = pango_font_description_from_string(font);
|
||||||
|
pango_layout_set_font_description(layout, desc);
|
||||||
|
pango_layout_set_single_paragraph_mode(layout, 1);
|
||||||
|
pango_layout_set_attributes(layout, attrs);
|
||||||
|
pango_attr_list_unref(attrs);
|
||||||
|
pango_font_description_free(desc);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
|
||||||
|
int *baseline, double scale, bool markup, const char *fmt, ...) {
|
||||||
|
char buf[max_chars];
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
if (vsnprintf(buf, sizeof(buf), fmt, args) >= max_chars) {
|
||||||
|
strcpy(&buf[sizeof(buf) - sizeof(overflow)], 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);
|
||||||
|
if (baseline) {
|
||||||
|
*baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
|
||||||
|
}
|
||||||
|
g_object_unref(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pango_printf(cairo_t *cairo, const char *font,
|
||||||
|
double scale, bool markup, const char *fmt, ...) {
|
||||||
|
char buf[max_chars];
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
if (vsnprintf(buf, sizeof(buf), fmt, args) >= max_chars) {
|
||||||
|
strcpy(&buf[sizeof(buf) - sizeof(overflow)], overflow);
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
|
||||||
|
cairo_font_options_t *fo = cairo_font_options_create();
|
||||||
|
cairo_get_font_options(cairo, fo);
|
||||||
|
pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo);
|
||||||
|
cairo_font_options_destroy(fo);
|
||||||
|
pango_cairo_update_layout(cairo, layout);
|
||||||
|
pango_cairo_show_layout(cairo, layout);
|
||||||
|
g_object_unref(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dmenu_draw(struct dmenu_panel *panel) {
|
||||||
|
|
||||||
|
cairo_t *cairo = panel->surface.cairo;
|
||||||
|
cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
|
||||||
|
cairo_paint(cairo);
|
||||||
|
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
|
||||||
|
struct monitor_info *m = panel->monitor;
|
||||||
|
double factor = m->scale / ((double)m->physical_width
|
||||||
|
/ m->logical_width);
|
||||||
|
|
||||||
|
int32_t width = round_to_int(m->physical_width * factor);
|
||||||
|
int32_t height = panel->height * m->scale;
|
||||||
|
|
||||||
|
if (panel->draw) {
|
||||||
|
panel->draw(cairo, width, height, m->scale);
|
||||||
|
}
|
||||||
|
wl_surface_attach(panel->surface.surface, panel->surface.buffer, 0, 0);
|
||||||
|
zwlr_layer_surface_v1_set_keyboard_interactivity(panel->surface.layer_surface, true);
|
||||||
|
wl_surface_damage(panel->surface.surface, 0, 0, m->logical_width, panel->height);
|
||||||
|
wl_surface_commit(panel->surface.surface);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel) {
|
||||||
|
switch (subpixel) {
|
||||||
|
case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
|
||||||
|
return CAIRO_SUBPIXEL_ORDER_RGB;
|
||||||
|
case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
|
||||||
|
return CAIRO_SUBPIXEL_ORDER_BGR;
|
||||||
|
case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
|
||||||
|
return CAIRO_SUBPIXEL_ORDER_VRGB;
|
||||||
|
case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
|
||||||
|
return CAIRO_SUBPIXEL_ORDER_VBGR;
|
||||||
|
default:
|
||||||
|
return CAIRO_SUBPIXEL_ORDER_DEFAULT;
|
||||||
|
}
|
||||||
|
return CAIRO_SUBPIXEL_ORDER_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
eprintf(const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
fprintf(stderr, "%s: ", progname);
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void layer_surface_configure(void *data,
|
||||||
|
struct zwlr_layer_surface_v1 *surface,
|
||||||
|
uint32_t serial, uint32_t width, uint32_t height) {
|
||||||
|
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void layer_surface_closed(void *_data,
|
||||||
|
struct zwlr_layer_surface_v1 *surface) {
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zwlr_layer_surface_v1_listener layer_surface_listener = {
|
||||||
|
.configure = layer_surface_configure,
|
||||||
|
.closed = layer_surface_closed,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int32_t subpixel;
|
||||||
|
int32_t physical_height;
|
||||||
|
|
||||||
|
|
||||||
|
static void output_geometry(void *data, struct wl_output *wl_output, int32_t x,
|
||||||
|
int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
|
||||||
|
const char *make, const char *model, int32_t transform) {
|
||||||
|
struct monitor_info *monitor = data;
|
||||||
|
monitor->subpixel = subpixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_mode(void *data, struct wl_output *wl_output, uint32_t flags,
|
||||||
|
int32_t width, int32_t height, int32_t refresh) {
|
||||||
|
struct monitor_info *monitor = data;
|
||||||
|
monitor->physical_width = width;
|
||||||
|
monitor->physical_height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_done(void *data, struct wl_output *wl_output) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_scale(void *data, struct wl_output *wl_output,
|
||||||
|
int32_t factor) {
|
||||||
|
struct monitor_info *monitor = data;
|
||||||
|
monitor->scale = factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_output_listener output_listener = {
|
||||||
|
.geometry = output_geometry,
|
||||||
|
.mode = output_mode,
|
||||||
|
.done = output_done,
|
||||||
|
.scale = output_scale,
|
||||||
|
};
|
||||||
|
static void xdg_output_handle_logical_position(void *data,
|
||||||
|
struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) {
|
||||||
|
// Who cares
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_output_handle_logical_size(void *data,
|
||||||
|
struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) {
|
||||||
|
struct monitor_info *monitor = data;
|
||||||
|
|
||||||
|
monitor->logical_width = width;
|
||||||
|
monitor->logical_height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_output_handle_done(void *data,
|
||||||
|
struct zxdg_output_v1 *xdg_output) {
|
||||||
|
// Who cares
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_output_handle_name(void *data,
|
||||||
|
struct zxdg_output_v1 *xdg_output, const char *name) {
|
||||||
|
struct monitor_info *monitor = data;
|
||||||
|
strncpy(monitor->name, name, MAX_MONITOR_NAME_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_output_handle_description(void *data,
|
||||||
|
struct zxdg_output_v1 *xdg_output, const char *description) {
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zxdg_output_v1_listener xdg_output_listener = {
|
||||||
|
.logical_position = xdg_output_handle_logical_position,
|
||||||
|
.logical_size = xdg_output_handle_logical_size,
|
||||||
|
.done = xdg_output_handle_done,
|
||||||
|
.name = xdg_output_handle_name,
|
||||||
|
.description = xdg_output_handle_description,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t format, int32_t fd, uint32_t size) {
|
||||||
|
|
||||||
|
struct dmenu_panel *panel = data;
|
||||||
|
|
||||||
|
panel->keyboard.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
|
|
||||||
|
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
|
||||||
|
close(fd);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
if (map_shm == MAP_FAILED) {
|
||||||
|
close(fd);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
panel->keyboard.xkb_keymap = xkb_keymap_new_from_string(
|
||||||
|
panel->keyboard.xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
|
||||||
|
munmap(map_shm, size);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
panel->keyboard.xkb_state = xkb_state_new(panel->keyboard.xkb_keymap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
||||||
|
// Who cares
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, struct wl_surface *surface) {
|
||||||
|
// Who cares
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_repeat(struct dmenu_panel *panel) {
|
||||||
|
if (panel->on_keyrepeat) {
|
||||||
|
panel->on_keyrepeat(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct itimerspec spec = { 0 };
|
||||||
|
spec.it_value.tv_sec = panel->repeat_period / 1000;
|
||||||
|
spec.it_value.tv_nsec = (panel->repeat_period % 1000) * 1000000l;
|
||||||
|
timerfd_settime(panel->repeat_timer, 0, &spec, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) {
|
||||||
|
struct dmenu_panel *panel = data;
|
||||||
|
|
||||||
|
enum wl_keyboard_key_state key_state = _key_state;
|
||||||
|
xkb_keysym_t sym = xkb_state_key_get_one_sym(panel->keyboard.xkb_state, key + 8);
|
||||||
|
if (panel->on_keyevent) {
|
||||||
|
panel->on_keyevent(panel, key_state, sym, panel->keyboard.control,
|
||||||
|
panel->keyboard.shift);
|
||||||
|
|
||||||
|
if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED && panel->repeat_period >= 0) {
|
||||||
|
panel->repeat_key_state = key_state;
|
||||||
|
panel->repeat_sym = sym;
|
||||||
|
|
||||||
|
struct itimerspec spec = { 0 };
|
||||||
|
spec.it_value.tv_sec = panel->repeat_delay / 1000;
|
||||||
|
spec.it_value.tv_nsec = (panel->repeat_delay % 1000) * 1000000l;
|
||||||
|
timerfd_settime(panel->repeat_timer, 0, &spec, NULL);
|
||||||
|
} else if (key_state == WL_KEYBOARD_KEY_STATE_RELEASED) {
|
||||||
|
struct itimerspec spec = { 0 };
|
||||||
|
timerfd_settime(panel->repeat_timer, 0, &spec, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
int32_t rate, int32_t delay) {
|
||||||
|
struct dmenu_panel *panel = data;
|
||||||
|
panel->repeat_delay = delay;
|
||||||
|
if (rate > 0) {
|
||||||
|
panel->repeat_period = 1000 / rate;
|
||||||
|
} else {
|
||||||
|
panel->repeat_period = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_modifiers (void *data, struct wl_keyboard *keyboard,
|
||||||
|
uint32_t serial, uint32_t mods_depressed,
|
||||||
|
uint32_t mods_latched, uint32_t mods_locked,
|
||||||
|
uint32_t group) {
|
||||||
|
struct dmenu_panel *panel = data;
|
||||||
|
xkb_state_update_mask(panel->keyboard.xkb_state,
|
||||||
|
mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
||||||
|
panel->keyboard.control = xkb_state_mod_name_is_active(panel->keyboard.xkb_state,
|
||||||
|
XKB_MOD_NAME_CTRL,
|
||||||
|
XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
|
||||||
|
panel->keyboard.shift = xkb_state_mod_name_is_active(panel->keyboard.xkb_state,
|
||||||
|
XKB_MOD_NAME_SHIFT,
|
||||||
|
XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
|
||||||
|
}
|
||||||
|
static const struct wl_keyboard_listener keyboard_listener = {
|
||||||
|
.keymap = keyboard_keymap,
|
||||||
|
.enter = keyboard_enter,
|
||||||
|
.leave = keyboard_leave,
|
||||||
|
.key = keyboard_key,
|
||||||
|
.modifiers = keyboard_modifiers,
|
||||||
|
.repeat_info = keyboard_repeat_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
||||||
|
enum wl_seat_capability caps) {
|
||||||
|
struct dmenu_panel *panel = data;
|
||||||
|
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
|
||||||
|
panel->keyboard.kbd = wl_seat_get_keyboard (panel->display_info.seat);
|
||||||
|
wl_keyboard_add_listener (panel->keyboard.kbd, &keyboard_listener, panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
void set_monitor_xdg_output(struct dmenu_panel *panel, struct monitor_info *monitor){
|
||||||
|
monitor->xdg_output =
|
||||||
|
zxdg_output_manager_v1_get_xdg_output(panel->display_info.xdg_output_manager,
|
||||||
|
monitor->output);
|
||||||
|
zxdg_output_v1_add_listener(monitor->xdg_output, &xdg_output_listener,
|
||||||
|
monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_global(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name, const char *interface, uint32_t version) {
|
||||||
|
struct dmenu_panel *panel = data;
|
||||||
|
|
||||||
|
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||||
|
panel->display_info.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
|
||||||
|
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||||
|
panel->display_info.seat = wl_registry_bind (registry, name, &wl_seat_interface, 4);
|
||||||
|
wl_seat_add_listener (panel->display_info.seat, &seat_listener, panel);
|
||||||
|
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||||
|
panel->surface.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
||||||
|
} else if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||||
|
|
||||||
|
if(n_monitors >= 16) return;
|
||||||
|
|
||||||
|
monitors[n_monitors] = malloc(sizeof(struct monitor_info));
|
||||||
|
monitors[n_monitors]->panel = panel;
|
||||||
|
memset(monitors[n_monitors]->name, 0, MAX_MONITOR_NAME_LEN);
|
||||||
|
monitors[n_monitors]->output = wl_registry_bind(registry, name, &wl_output_interface, 2);
|
||||||
|
|
||||||
|
wl_output_add_listener(monitors[n_monitors]->output, &output_listener,
|
||||||
|
monitors[n_monitors]);
|
||||||
|
|
||||||
|
if (panel->display_info.xdg_output_manager != NULL) {
|
||||||
|
set_monitor_xdg_output(panel, monitors[n_monitors]);
|
||||||
|
}
|
||||||
|
n_monitors++;
|
||||||
|
|
||||||
|
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||||
|
panel->surface.layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
|
||||||
|
|
||||||
|
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
|
||||||
|
panel->display_info.xdg_output_manager = wl_registry_bind(registry, name,
|
||||||
|
&zxdg_output_manager_v1_interface, 2);
|
||||||
|
|
||||||
|
for(int m = 0; m < n_monitors; m++){
|
||||||
|
set_monitor_xdg_output(panel, monitors[m]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_global_remove(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_buffer_listener buffer_listener = {
|
||||||
|
.release = buffer_release
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wl_buffer *dmenu_create_buffer(struct dmenu_panel *panel) {
|
||||||
|
struct monitor_info *m = panel->monitor;
|
||||||
|
double factor = m->scale / ((double)m->physical_width
|
||||||
|
/ m->logical_width);
|
||||||
|
|
||||||
|
int32_t width = round_to_int(m->physical_width * factor);
|
||||||
|
int32_t height = round_to_int(panel->height * m->scale);
|
||||||
|
|
||||||
|
int stride = width * 4;
|
||||||
|
int size = stride * height;
|
||||||
|
|
||||||
|
int fd = create_shm_file(size);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
panel->surface.shm_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if (panel->surface.shm_data == MAP_FAILED) {
|
||||||
|
fprintf(stderr, "mmap failed: %m\n");
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_shm_pool *pool = wl_shm_create_pool(panel->surface.shm, fd, size);
|
||||||
|
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
|
||||||
|
stride, WL_SHM_FORMAT_ARGB8888);
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
|
||||||
|
|
||||||
|
wl_buffer_add_listener(buffer, &buffer_listener, panel);
|
||||||
|
|
||||||
|
cairo_surface_t *s = cairo_image_surface_create_for_data(panel->surface.shm_data,
|
||||||
|
CAIRO_FORMAT_ARGB32,
|
||||||
|
width, height, width * 4);
|
||||||
|
panel->surface.cairo = cairo_create(s);
|
||||||
|
cairo_set_antialias(panel->surface.cairo, CAIRO_ANTIALIAS_BEST);
|
||||||
|
cairo_font_options_t *fo = cairo_font_options_create();
|
||||||
|
cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
|
||||||
|
cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
|
||||||
|
cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(m->subpixel));
|
||||||
|
cairo_set_font_options(panel->surface.cairo, fo);
|
||||||
|
cairo_font_options_destroy(fo);
|
||||||
|
cairo_save(panel->surface.cairo);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = handle_global,
|
||||||
|
.global_remove = handle_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void dmenu_init_panel(struct dmenu_panel *panel, int32_t height, bool bottom) {
|
||||||
|
if(!setlocale(LC_CTYPE, ""))
|
||||||
|
weprintf("no locale support\n");
|
||||||
|
|
||||||
|
if(!(panel->display_info.display = wl_display_connect(NULL)))
|
||||||
|
eprintf("cannot open display\n");
|
||||||
|
|
||||||
|
if ((panel->repeat_timer = timerfd_create(CLOCK_MONOTONIC, 0)) < 0)
|
||||||
|
eprintf("cannot create timer fd\n");
|
||||||
|
|
||||||
|
panel->height = height;
|
||||||
|
panel->keyboard.control = false;
|
||||||
|
panel->on_keyevent = NULL;
|
||||||
|
|
||||||
|
struct wl_registry *registry = wl_display_get_registry(panel->display_info.display);
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener, panel);
|
||||||
|
|
||||||
|
wl_display_roundtrip(panel->display_info.display);
|
||||||
|
|
||||||
|
/* Second roundtrip for xdg-output. Will populate display dimensions. */
|
||||||
|
wl_display_roundtrip(panel->display_info.display);
|
||||||
|
|
||||||
|
|
||||||
|
panel->surface.surface = wl_compositor_create_surface(panel->display_info.compositor);
|
||||||
|
|
||||||
|
panel->monitor = NULL;
|
||||||
|
if (!panel->selected_monitor_name) {
|
||||||
|
panel->monitor = monitors[panel->selected_monitor];
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < n_monitors; ++i) {
|
||||||
|
if (monitors[i] && !strncmp(panel->selected_monitor_name,
|
||||||
|
monitors[i]->name,
|
||||||
|
MAX_MONITOR_NAME_LEN)) {
|
||||||
|
panel->monitor = monitors[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!panel->monitor) {
|
||||||
|
if (!panel->selected_monitor_name)
|
||||||
|
eprintf("No monitor with index %i available.\n", panel->selected_monitor);
|
||||||
|
else
|
||||||
|
eprintf("No monitor with name %s available.\n", panel->selected_monitor_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
panel->surface.buffer = dmenu_create_buffer(panel);
|
||||||
|
|
||||||
|
if (!panel->surface.layer_shell)
|
||||||
|
eprintf("Compositor does not implement wlr-layer-shell protocol.\n");
|
||||||
|
panel->surface.layer_surface =
|
||||||
|
zwlr_layer_shell_v1_get_layer_surface(panel->surface.layer_shell,
|
||||||
|
panel->surface.surface,
|
||||||
|
panel->monitor->output,
|
||||||
|
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
|
||||||
|
"panel");
|
||||||
|
|
||||||
|
zwlr_layer_surface_v1_set_size(panel->surface.layer_surface,
|
||||||
|
panel->monitor->logical_width, panel->height);
|
||||||
|
zwlr_layer_surface_v1_set_anchor(panel->surface.layer_surface,
|
||||||
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
|
||||||
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
|
||||||
|
(bottom ? ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
|
||||||
|
: ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP));
|
||||||
|
|
||||||
|
|
||||||
|
zwlr_layer_surface_v1_add_listener(panel->surface.layer_surface,
|
||||||
|
&layer_surface_listener, panel);
|
||||||
|
|
||||||
|
wl_surface_set_buffer_scale(panel->surface.surface,
|
||||||
|
panel->monitor->scale);
|
||||||
|
wl_surface_commit(panel->surface.surface);
|
||||||
|
wl_display_roundtrip(panel->display_info.display);
|
||||||
|
|
||||||
|
zwlr_layer_surface_v1_set_keyboard_interactivity(panel->surface.layer_surface, true);
|
||||||
|
|
||||||
|
wl_surface_attach(panel->surface.surface, panel->surface.buffer, 0, 0);
|
||||||
|
wl_surface_commit(panel->surface.surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmenu_show(struct dmenu_panel *dmenu) {
|
||||||
|
dmenu_draw(dmenu);
|
||||||
|
|
||||||
|
zwlr_layer_surface_v1_set_keyboard_interactivity(dmenu->surface.layer_surface, true);
|
||||||
|
wl_surface_commit(dmenu->surface.surface);
|
||||||
|
|
||||||
|
struct pollfd fds[] = {
|
||||||
|
{ wl_display_get_fd(dmenu->display_info.display), POLLIN },
|
||||||
|
{ dmenu->repeat_timer, POLLIN },
|
||||||
|
};
|
||||||
|
const int nfds = sizeof(fds) / sizeof(*fds);
|
||||||
|
|
||||||
|
wl_display_flush(dmenu->display_info.display);
|
||||||
|
|
||||||
|
dmenu->running = true;
|
||||||
|
while (dmenu->running) {
|
||||||
|
if (wl_display_flush(dmenu->display_info.display) < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (poll(fds, nfds, -1) < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds[0].revents & POLLIN) {
|
||||||
|
if (wl_display_dispatch(dmenu->display_info.display) < 0) {
|
||||||
|
dmenu->running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds[1].revents & POLLIN) {
|
||||||
|
keyboard_repeat(dmenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dmenu_close called */
|
||||||
|
wl_display_disconnect(dmenu->display_info.display);
|
||||||
|
|
||||||
|
}
|
||||||
|
void dmenu_close(struct dmenu_panel *dmenu) {
|
||||||
|
dmenu->running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
weprintf(const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
fprintf(stderr, "%s: warning: ", progname);
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
|
#define MAX_MONITOR_NAME_LEN 255
|
||||||
|
|
||||||
|
#define FG(dc, col) ((col)[(dc)->invert ? ColBG : ColFG])
|
||||||
|
#define BG(dc, col) ((col)[(dc)->invert ? ColFG : ColBG])
|
||||||
|
|
||||||
|
enum { ColBG, ColFG, ColBorder, ColLast };
|
||||||
|
|
||||||
|
struct dmenu_panel;
|
||||||
|
|
||||||
|
struct monitor_info {
|
||||||
|
int32_t physical_width;
|
||||||
|
int32_t physical_height;
|
||||||
|
int32_t logical_width;
|
||||||
|
int32_t logical_height;
|
||||||
|
double scale;
|
||||||
|
|
||||||
|
char name[MAX_MONITOR_NAME_LEN];
|
||||||
|
|
||||||
|
enum wl_output_subpixel subpixel;
|
||||||
|
|
||||||
|
struct zxdg_output_v1 *xdg_output;
|
||||||
|
|
||||||
|
struct wl_output *output;
|
||||||
|
struct dmenu_panel *panel;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct monitor_info *monitors[];
|
||||||
|
|
||||||
|
struct display_info {
|
||||||
|
struct zxdg_output_manager_v1 *xdg_output_manager;
|
||||||
|
struct wl_display * display;
|
||||||
|
struct wl_compositor *compositor;
|
||||||
|
struct wl_seat *seat;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct keyboard_info {
|
||||||
|
struct wl_keyboard *kbd;
|
||||||
|
struct xkb_context *xkb_context;
|
||||||
|
struct xkb_keymap *xkb_keymap;
|
||||||
|
struct xkb_state *xkb_state;
|
||||||
|
bool control;
|
||||||
|
bool shift;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct surface {
|
||||||
|
cairo_t *cairo;
|
||||||
|
struct wl_buffer *buffer;
|
||||||
|
struct wl_surface *surface;
|
||||||
|
struct wl_shm *shm;
|
||||||
|
void *shm_data;
|
||||||
|
struct zwlr_layer_shell_v1 *layer_shell;
|
||||||
|
struct zwlr_layer_surface_v1 *layer_surface;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dmenu_panel {
|
||||||
|
struct keyboard_info keyboard;
|
||||||
|
/* struct monitor_info monitor; */
|
||||||
|
int selected_monitor;
|
||||||
|
char *selected_monitor_name;
|
||||||
|
|
||||||
|
struct monitor_info *monitor;
|
||||||
|
struct display_info display_info;
|
||||||
|
|
||||||
|
struct surface surface;
|
||||||
|
|
||||||
|
void (*on_keyevent)(struct dmenu_panel *,enum wl_keyboard_key_state,
|
||||||
|
xkb_keysym_t, bool, bool);
|
||||||
|
void (*on_keyrepeat)(struct dmenu_panel *);
|
||||||
|
|
||||||
|
void (*draw)(cairo_t *, int32_t, int32_t, int32_t);
|
||||||
|
|
||||||
|
int32_t width;
|
||||||
|
int32_t height;
|
||||||
|
|
||||||
|
int repeat_timer;
|
||||||
|
int repeat_delay;
|
||||||
|
int repeat_period;
|
||||||
|
enum wl_keyboard_key_state repeat_key_state;
|
||||||
|
xkb_keysym_t repeat_sym;
|
||||||
|
|
||||||
|
bool running;
|
||||||
|
};
|
||||||
|
|
||||||
|
void dmenu_init_panel(struct dmenu_panel *, int32_t, bool);
|
||||||
|
void dmenu_draw(struct dmenu_panel *);
|
||||||
|
void dmenu_show(struct dmenu_panel *);
|
||||||
|
void dmenu_close(struct dmenu_panel *);
|
||||||
|
|
||||||
|
|
||||||
|
void pango_printf(cairo_t *cairo, const char *font,
|
||||||
|
double scale, bool markup, const char *fmt, ...);
|
||||||
|
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
|
||||||
|
int *baseline, double scale, bool markup, const char *fmt, ...);
|
||||||
|
void eprintf(const char *fmt, ...);
|
||||||
|
void weprintf(const char *fmt, ...);
|
||||||
|
int32_t round_to_int(double val);
|
||||||
|
|
||||||
|
extern const char *progname;
|
|
@ -0,0 +1,82 @@
|
||||||
|
project(
|
||||||
|
'dmenu-wl',
|
||||||
|
'c',
|
||||||
|
version: '4.2.1',
|
||||||
|
license: 'MIT',
|
||||||
|
default_options: ['c_std=c99']
|
||||||
|
)
|
||||||
|
|
||||||
|
add_project_arguments(
|
||||||
|
[
|
||||||
|
'-pedantic',
|
||||||
|
'-Wall',
|
||||||
|
'-D_DEFAULT_SOURCE',
|
||||||
|
'-DVERSION="@0@"'.format(meson.project_version()),
|
||||||
|
],
|
||||||
|
language : 'c'
|
||||||
|
)
|
||||||
|
|
||||||
|
cc = meson.get_compiler('c')
|
||||||
|
|
||||||
|
cairo = dependency('cairo')
|
||||||
|
glib = dependency('glib-2.0')
|
||||||
|
gobject = dependency('gobject-2.0')
|
||||||
|
pango = dependency('pango')
|
||||||
|
pangocairo = dependency('pangocairo')
|
||||||
|
rt = cc.find_library('rt')
|
||||||
|
wayland_client = dependency('wayland-client')
|
||||||
|
wayland_protos = dependency('wayland-protocols')
|
||||||
|
wayland_scanner_dep = dependency('wayland-scanner')
|
||||||
|
wayland_scanner = find_program(
|
||||||
|
wayland_scanner_dep.get_pkgconfig_variable('wayland_scanner')
|
||||||
|
)
|
||||||
|
xkbcommon = dependency('xkbcommon')
|
||||||
|
|
||||||
|
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
|
||||||
|
|
||||||
|
protocols = [
|
||||||
|
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
|
||||||
|
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||||
|
'wlr-layer-shell-unstable-v1.xml'
|
||||||
|
]
|
||||||
|
|
||||||
|
protos_src = []
|
||||||
|
protos_headers = []
|
||||||
|
|
||||||
|
foreach p : protocols
|
||||||
|
xml = join_paths(p)
|
||||||
|
protos_src += custom_target(
|
||||||
|
xml.underscorify() + '_client_c',
|
||||||
|
input: xml,
|
||||||
|
output: '@BASENAME@-protocol.c',
|
||||||
|
command: [wayland_scanner, 'public-code', '@INPUT@', '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
protos_headers += custom_target(
|
||||||
|
xml.underscorify() + '_client_h',
|
||||||
|
input: xml,
|
||||||
|
output: '@BASENAME@-client-protocol.h',
|
||||||
|
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
dmenu_deps = [
|
||||||
|
cairo,
|
||||||
|
glib,
|
||||||
|
gobject,
|
||||||
|
pango,
|
||||||
|
pangocairo,
|
||||||
|
rt,
|
||||||
|
wayland_client,
|
||||||
|
xkbcommon
|
||||||
|
]
|
||||||
|
|
||||||
|
dmenu_src = ['dmenu.c', 'draw.c'] + protos_src + protos_headers
|
||||||
|
|
||||||
|
executable('dmenu-wl', dmenu_src, dependencies: dmenu_deps, install: true)
|
||||||
|
|
||||||
|
executable('dmenu-wl_path', 'dmenu_path.c', install: true)
|
||||||
|
|
||||||
|
install_data('dmenu-wl_run', install_dir: get_option('bindir'))
|
||||||
|
|
||||||
|
install_man('dmenu-wl.1')
|
||||||
|
|
|
@ -0,0 +1,285 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="wlr_layer_shell_unstable_v1">
|
||||||
|
<copyright>
|
||||||
|
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.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="zwlr_layer_shell_v1" version="1">
|
||||||
|
<description summary="create surfaces that are layers of the desktop">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="get_layer_surface">
|
||||||
|
<description summary="create a layer_surface from a surface">
|
||||||
|
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.
|
||||||
|
|
||||||
|
You may pass NULL for output to allow the compositor to decide which
|
||||||
|
output to use. Generally this will be the one that the user most
|
||||||
|
recently interacted with.
|
||||||
|
|
||||||
|
Clients can specify a namespace that defines the purpose of the layer
|
||||||
|
surface.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
|
||||||
|
<arg name="surface" type="object" interface="wl_surface"/>
|
||||||
|
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||||
|
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
|
||||||
|
<arg name="namespace" type="string" summary="namespace for the layer surface"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="role" value="0" summary="wl_surface has another role"/>
|
||||||
|
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
|
||||||
|
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="layer">
|
||||||
|
<description summary="available layers for surfaces">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<entry name="background" value="0"/>
|
||||||
|
<entry name="bottom" value="1"/>
|
||||||
|
<entry name="top" value="2"/>
|
||||||
|
<entry name="overlay" value="3"/>
|
||||||
|
</enum>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwlr_layer_surface_v1" version="1">
|
||||||
|
<description summary="layer metadata interface">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="set_size">
|
||||||
|
<description summary="sets the size of the surface">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="width" type="uint"/>
|
||||||
|
<arg name="height" type="uint"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_anchor">
|
||||||
|
<description summary="configures the anchor point of the surface">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="anchor" type="uint" enum="anchor"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_exclusive_zone">
|
||||||
|
<description summary="configures the exclusive geometry of this surface">
|
||||||
|
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 accommodate 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.
|
||||||
|
</description>
|
||||||
|
<arg name="zone" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_margin">
|
||||||
|
<description summary="sets a margin from the anchor point">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="top" type="int"/>
|
||||||
|
<arg name="right" type="int"/>
|
||||||
|
<arg name="bottom" type="int"/>
|
||||||
|
<arg name="left" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_keyboard_interactivity">
|
||||||
|
<description summary="requests keyboard events">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="keyboard_interactivity" type="uint"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_popup">
|
||||||
|
<description summary="assign this layer_surface as an xdg_popup parent">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="popup" type="object" interface="xdg_popup"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="ack_configure">
|
||||||
|
<description summary="ack a configure event">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint" summary="the serial from the configure event"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the layer_surface">
|
||||||
|
This request destroys the layer surface.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="configure">
|
||||||
|
<description summary="suggest a surface change">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint"/>
|
||||||
|
<arg name="width" type="uint"/>
|
||||||
|
<arg name="height" type="uint"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="closed">
|
||||||
|
<description summary="surface should be closed">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
|
||||||
|
<entry name="invalid_size" value="1" summary="size is invalid"/>
|
||||||
|
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="anchor" bitfield="true">
|
||||||
|
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
|
||||||
|
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
|
||||||
|
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
||||||
|
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
||||||
|
</enum>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
Reference in New Issue