mirror of
https://github.com/Cloudef/bemenu
synced 2024-06-01 04:16:21 +02:00
Cleanup wayland renderer and plugin support.
This commit is contained in:
parent
f8d97efb8f
commit
3f5e21a83f
|
@ -0,0 +1,77 @@
|
|||
#=============================================================================
|
||||
# Copyright (C) 2012-2013 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# * Neither the name of Pier Luigi Fiorini nor the names of his
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#=============================================================================
|
||||
|
||||
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
||||
|
||||
# wayland_add_protocol_client(outfiles inputfile basename)
|
||||
function(WAYLAND_ADD_PROTOCOL_CLIENT _sources _protocol _basename)
|
||||
if(NOT WAYLAND_SCANNER_EXECUTABLE)
|
||||
message(FATAL "The wayland-scanner executable has nto been found on your system. You must install it.")
|
||||
endif()
|
||||
|
||||
get_filename_component(_infile ${_protocol} ABSOLUTE)
|
||||
set(_client_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.h")
|
||||
set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-protocol.c")
|
||||
|
||||
add_custom_command(OUTPUT "${_client_header}"
|
||||
COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header < ${_infile} > ${_client_header}
|
||||
DEPENDS ${_infile} VERBATIM)
|
||||
|
||||
add_custom_command(OUTPUT "${_code}"
|
||||
COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code < ${_infile} > ${_code}
|
||||
DEPENDS ${_infile} VERBATIM)
|
||||
|
||||
list(APPEND ${_sources} "${_client_header}" "${_code}")
|
||||
set(${_sources} ${${_sources}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# wayland_add_protocol_server(outfiles inputfile basename)
|
||||
function(WAYLAND_ADD_PROTOCOL_SERVER _sources _protocol _basename)
|
||||
if(NOT WAYLAND_SCANNER_EXECUTABLE)
|
||||
message(FATAL "The wayland-scanner executable has nto been found on your system. You must install it.")
|
||||
endif()
|
||||
|
||||
get_filename_component(_infile ${_protocol} ABSOLUTE)
|
||||
set(_server_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-server-protocol.h")
|
||||
set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-protocol.c")
|
||||
|
||||
add_custom_command(OUTPUT "${_server_header}"
|
||||
COMMAND ${WAYLAND_SCANNER_EXECUTABLE} server-header < ${_infile} > ${_server_header}
|
||||
DEPENDS ${_infile} VERBATIM)
|
||||
|
||||
add_custom_command(OUTPUT "${_code}"
|
||||
COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code < ${_infile} > ${_code}
|
||||
DEPENDS ${_infile} VERBATIM)
|
||||
|
||||
list(APPEND ${_sources} "${_server_header}" "${_code}")
|
||||
set(${_sources} ${${_sources}} PARENT_SCOPE)
|
||||
endfunction()
|
|
@ -1,4 +1,4 @@
|
|||
#define _XOPEN_SOURCE 500
|
||||
#define _DEFAULT_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
@ -9,19 +9,23 @@
|
|||
#include <bemenu.h>
|
||||
|
||||
static struct {
|
||||
enum bm_prioritory prioritory;
|
||||
enum bm_filter_mode filter_mode;
|
||||
int wrap;
|
||||
unsigned int lines;
|
||||
int32_t wrap;
|
||||
uint32_t lines;
|
||||
const char *title;
|
||||
int selected;
|
||||
int bottom;
|
||||
int grab;
|
||||
int monitor;
|
||||
const char *renderer;
|
||||
int32_t selected;
|
||||
int32_t bottom;
|
||||
int32_t grab;
|
||||
int32_t monitor;
|
||||
} client = {
|
||||
.prioritory = BM_PRIO_ANY,
|
||||
.filter_mode = BM_FILTER_MODE_DMENU,
|
||||
.wrap = 0,
|
||||
.lines = 0,
|
||||
.title = "bemenu",
|
||||
.renderer = NULL,
|
||||
.selected = 0,
|
||||
.bottom = 0,
|
||||
.grab = 0,
|
||||
|
@ -84,10 +88,12 @@ usage(FILE *out, const char *name)
|
|||
" -w, --wrap wraps cursor selection.\n"
|
||||
" -l, --list list items vertically with the given number of lines.\n"
|
||||
" -p, --prompt defines the prompt text to be displayed.\n"
|
||||
" -I, --index select item at index automatically.\n\n"
|
||||
" -I, --index select item at index automatically.\n"
|
||||
" --backend options: curses, wayland\n"
|
||||
" --prioritory options: terminal, gui\n\n"
|
||||
|
||||
"Backend specific options\n"
|
||||
" c = ncurses\n" // x == x11
|
||||
" c = ncurses, w == wayland\n"
|
||||
" (...) At end of help indicates the backend support for option.\n\n"
|
||||
|
||||
" -b, --bottom appears at the bottom of the screen. ()\n"
|
||||
|
@ -113,17 +119,19 @@ parse_args(int *argc, char **argv[])
|
|||
{ "list", required_argument, 0, 'l' },
|
||||
{ "prompt", required_argument, 0, 'p' },
|
||||
{ "index", required_argument, 0, 'I' },
|
||||
{ "backend", required_argument, 0, 0x100 },
|
||||
{ "prioritory", required_argument, 0, 0x101 },
|
||||
|
||||
{ "bottom", no_argument, 0, 'b' },
|
||||
{ "grab", no_argument, 0, 'f' },
|
||||
{ "monitor", required_argument, 0, 'm' },
|
||||
{ "fn", required_argument, 0, 0x100 },
|
||||
{ "nb", required_argument, 0, 0x101 },
|
||||
{ "nf", required_argument, 0, 0x102 },
|
||||
{ "sb", required_argument, 0, 0x103 },
|
||||
{ "sf", required_argument, 0, 0x104 },
|
||||
{ "fn", required_argument, 0, 0x102 },
|
||||
{ "nb", required_argument, 0, 0x103 },
|
||||
{ "nf", required_argument, 0, 0x104 },
|
||||
{ "sb", required_argument, 0, 0x105 },
|
||||
{ "sf", required_argument, 0, 0x106 },
|
||||
|
||||
{ "disco", no_argument, 0, 0x105 },
|
||||
{ "disco", no_argument, 0, 0x107 },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -160,6 +168,17 @@ parse_args(int *argc, char **argv[])
|
|||
client.selected = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
|
||||
case 0x100:
|
||||
client.renderer = optarg;
|
||||
break;
|
||||
|
||||
case 0x101:
|
||||
if (!strcmp(optarg, "terminal"))
|
||||
client.prioritory = BM_PRIO_TERMINAL;
|
||||
else if (!strcmp(optarg, "gui"))
|
||||
client.prioritory = BM_PRIO_GUI;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
client.bottom = 1;
|
||||
break;
|
||||
|
@ -170,14 +189,14 @@ parse_args(int *argc, char **argv[])
|
|||
client.monitor = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
|
||||
case 0x100:
|
||||
case 0x101:
|
||||
case 0x102:
|
||||
case 0x103:
|
||||
case 0x104:
|
||||
case 0x105:
|
||||
case 0x106:
|
||||
break;
|
||||
|
||||
case 0x105:
|
||||
case 0x107:
|
||||
disco();
|
||||
break;
|
||||
|
||||
|
@ -241,7 +260,7 @@ main(int argc, char **argv)
|
|||
parse_args(&argc, &argv);
|
||||
|
||||
struct bm_menu *menu;
|
||||
if (!(menu = bm_menu_new(NULL)))
|
||||
if (!(menu = bm_menu_new(client.renderer, client.prioritory)))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
bm_menu_set_title(menu, client.title);
|
||||
|
|
|
@ -29,11 +29,13 @@ ENDIF ()
|
|||
STRING(REGEX MATCHALL "[0-9]+" VERSION_COMPONENTS ${BEMENU_VERSION})
|
||||
LIST(GET VERSION_COMPONENTS 0 SOVERSION)
|
||||
|
||||
# Add include directories
|
||||
INCLUDE_DIRECTORIES(${BEMENU_INCLUDE} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Compile renderer plugins
|
||||
ADD_SUBDIRECTORY(renderers)
|
||||
|
||||
# Compile
|
||||
INCLUDE_DIRECTORIES(${BEMENU_INCLUDE} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
ADD_LIBRARY(bemenu SHARED ${BEMENU_SOURCE})
|
||||
SET_TARGET_PROPERTIES(bemenu PROPERTIES
|
||||
VERSION ${BEMENU_VERSION}
|
||||
|
|
37
lib/bemenu.h
37
lib/bemenu.h
|
@ -1,3 +1,6 @@
|
|||
#ifndef _BEMENU_H_
|
||||
#define _BEMENU_H_
|
||||
|
||||
/**
|
||||
* @file bemenu.h
|
||||
*
|
||||
|
@ -79,6 +82,27 @@ const char* bm_version(void);
|
|||
* @addtogroup Renderer
|
||||
* @{ */
|
||||
|
||||
/**
|
||||
* Prioritories for renderer plugins.
|
||||
*/
|
||||
enum bm_prioritory {
|
||||
/**
|
||||
* Do not use this in renderers.
|
||||
* This flag is for bm_menu_new, if any renderer is fine.
|
||||
*/
|
||||
BM_PRIO_ANY,
|
||||
|
||||
/**
|
||||
* Renderer runs in terminal.
|
||||
*/
|
||||
BM_PRIO_TERMINAL,
|
||||
|
||||
/**
|
||||
* Renderer runs in GUI.
|
||||
*/
|
||||
BM_PRIO_GUI,
|
||||
};
|
||||
|
||||
/**
|
||||
* Get name of the renderer.
|
||||
*
|
||||
|
@ -87,6 +111,14 @@ const char* bm_version(void);
|
|||
*/
|
||||
const char* bm_renderer_get_name(const struct bm_renderer *renderer);
|
||||
|
||||
/**
|
||||
* Get prioritory of the renderer.
|
||||
*
|
||||
* @param renderer bm_renderer instance.
|
||||
* @return bm_prioritory enum value.
|
||||
*/
|
||||
enum bm_prioritory bm_renderer_get_prioritory(const struct bm_renderer *renderer);
|
||||
|
||||
/**
|
||||
* @} Renderer */
|
||||
|
||||
|
@ -158,9 +190,10 @@ enum bm_key {
|
|||
* Create new bm_menu instance.
|
||||
*
|
||||
* @param renderer Name of renderer to be used for this instance, pass **NULL** for auto-detection.
|
||||
* @param prioritory @link ::bm_prioritory @endlink enum for which kind of renderer is wanted. Pass BM_PRIO_ANY, for anything.
|
||||
* @return bm_menu for new menu instance, **NULL** if creation failed.
|
||||
*/
|
||||
struct bm_menu* bm_menu_new(const char *renderer);
|
||||
struct bm_menu* bm_menu_new(const char *renderer, enum bm_prioritory prioritory);
|
||||
|
||||
/**
|
||||
* Release bm_menu instance.
|
||||
|
@ -512,4 +545,6 @@ const char* bm_item_get_text(const struct bm_item *item);
|
|||
|
||||
/** @} Item */
|
||||
|
||||
#endif /* _BEMENU_H_ */
|
||||
|
||||
/* vim: set ts=8 sw=4 tw=0 :*/
|
||||
|
|
|
@ -1,9 +1,20 @@
|
|||
#ifndef _BEMENU_INTERNAL_H_
|
||||
#define _BEMENU_INTERNAL_H_
|
||||
|
||||
#include "bemenu.h"
|
||||
|
||||
#if __GNUC__
|
||||
# define BM_LOG_ATTR(x, y) __attribute__((format(printf, x, y)))
|
||||
#else
|
||||
# define BM_LOG_ATTR(x, y)
|
||||
#endif
|
||||
|
||||
#ifndef size_t
|
||||
# include <stddef.h> /* for size_t */
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* Destructor function pointer for some list calls.
|
||||
*/
|
||||
|
@ -59,6 +70,18 @@ struct render_api {
|
|||
* Tells underlying renderer to draw the menu.
|
||||
*/
|
||||
void (*render)(const struct bm_menu *menu);
|
||||
|
||||
/**
|
||||
* Version of the plugin.
|
||||
* Should match BM_VERSION or failure.
|
||||
*/
|
||||
const char *version;
|
||||
|
||||
/**
|
||||
* Prioritory of the plugin.
|
||||
* Terminal renderers should be first, then graphicals.
|
||||
*/
|
||||
enum bm_prioritory prioritory;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -80,6 +103,12 @@ struct bm_renderer {
|
|||
*/
|
||||
void *handle;
|
||||
|
||||
/**
|
||||
* Data used by the renderer internally.
|
||||
* Nobody else should touch this.
|
||||
*/
|
||||
void *internal;
|
||||
|
||||
/**
|
||||
* API
|
||||
*/
|
||||
|
@ -117,7 +146,7 @@ struct bm_menu {
|
|||
/**
|
||||
* Underlying renderer access.
|
||||
*/
|
||||
const struct bm_renderer *renderer;
|
||||
struct bm_renderer *renderer;
|
||||
|
||||
/**
|
||||
* Items contained in menu instance.
|
||||
|
@ -202,9 +231,13 @@ bool list_add_item_at(struct list *list, void *item, uint32_t index);
|
|||
bool list_add_item(struct list *list, void *item);
|
||||
bool list_remove_item_at(struct list *list, uint32_t index);
|
||||
bool list_remove_item(struct list *list, const void *item);
|
||||
void list_sort(struct list *list, int (*compar)(const void *a, const void *b));
|
||||
|
||||
/* util.c */
|
||||
char* bm_strdup(const char *s);
|
||||
bool bm_resize_buffer(char **in_out_buffer, size_t *in_out_size, size_t nsize);
|
||||
BM_LOG_ATTR(1, 2) char* bm_dprintf(const char *fmt, ...);
|
||||
bool bm_vrprintf(char **in_out_buffer, size_t *in_out_len, const char *fmt, va_list args);
|
||||
size_t bm_strip_token(char *string, const char *token, size_t *out_next);
|
||||
int bm_strupcmp(const char *hay, const char *needle);
|
||||
int bm_strnupcmp(const char *hay, const char *needle, size_t len);
|
||||
|
@ -217,4 +250,6 @@ size_t bm_utf8_rune_remove(char *string, size_t start, size_t *out_rune_width);
|
|||
size_t bm_utf8_rune_insert(char **string, size_t *bufSize, size_t start, const char *rune, uint32_t u8len, size_t *out_rune_width);
|
||||
size_t bm_unicode_insert(char **string, size_t *bufSize, size_t start, uint32_t unicode, size_t *out_rune_width);
|
||||
|
||||
#endif /* _BEMENU_INTERNAL_H_ */
|
||||
|
||||
/* vim: set ts=8 sw=4 tw=0 :*/
|
||||
|
|
|
@ -36,10 +36,16 @@ load(const char *file, struct bm_renderer *renderer)
|
|||
if (!(regfun = chckDlLoadSymbol(handle, "register_renderer", &error)))
|
||||
goto fail;
|
||||
|
||||
renderer->file = bm_strdup(file);
|
||||
const char *name;
|
||||
if (!(name = regfun(&renderer->api)))
|
||||
goto fail;
|
||||
|
||||
if (strcmp(renderer->api.version, BM_VERSION))
|
||||
goto fail;
|
||||
|
||||
renderer->handle = handle;
|
||||
const char *name = regfun(&renderer->api);
|
||||
renderer->name = (name ? bm_strdup(name) : NULL);
|
||||
renderer->name = bm_strdup(name);
|
||||
renderer->file = bm_strdup(file);
|
||||
return true;
|
||||
|
||||
fail:
|
||||
|
@ -49,6 +55,13 @@ fail:
|
|||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
compare(const void *a, const void *b)
|
||||
{
|
||||
const struct bm_renderer *ra = *(struct bm_renderer**)a, *rb = *(struct bm_renderer**)b;
|
||||
return (ra->api.prioritory > rb->api.prioritory);
|
||||
}
|
||||
|
||||
static bool
|
||||
load_to_list(const char *file)
|
||||
{
|
||||
|
@ -60,7 +73,12 @@ load_to_list(const char *file)
|
|||
goto fail;
|
||||
|
||||
chckDlUnload(renderer->handle);
|
||||
return list_add_item(&renderers, renderer);
|
||||
|
||||
if (!list_add_item(&renderers, renderer))
|
||||
goto fail;
|
||||
|
||||
list_sort(&renderers, compare);
|
||||
return true;
|
||||
|
||||
fail:
|
||||
if (renderer)
|
||||
|
@ -76,6 +94,8 @@ bm_renderer_activate(struct bm_renderer *renderer, struct bm_menu *menu)
|
|||
if (!load(renderer->file, renderer))
|
||||
return false;
|
||||
|
||||
menu->renderer = renderer;
|
||||
|
||||
if (!renderer->api.constructor(menu))
|
||||
goto fail;
|
||||
|
||||
|
@ -83,6 +103,7 @@ bm_renderer_activate(struct bm_renderer *renderer, struct bm_menu *menu)
|
|||
|
||||
fail:
|
||||
chckDlUnload(renderer->handle);
|
||||
menu->renderer = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -155,4 +176,11 @@ bm_renderer_get_name(const struct bm_renderer *renderer)
|
|||
return renderer->name;
|
||||
}
|
||||
|
||||
enum bm_prioritory
|
||||
bm_renderer_get_prioritory(const struct bm_renderer *renderer)
|
||||
{
|
||||
assert(renderer);
|
||||
return renderer->api.prioritory;
|
||||
}
|
||||
|
||||
/* vim: set ts=8 sw=4 tw=0 :*/
|
||||
|
|
20
lib/list.c
20
lib/list.c
|
@ -79,15 +79,8 @@ list_grow(struct list *list, uint32_t step)
|
|||
void *tmp;
|
||||
uint32_t nsize = sizeof(struct bm_item*) * (list->allocated + step);
|
||||
|
||||
if (!list->items || !(tmp = realloc(list->items, nsize))) {
|
||||
if (!(tmp = malloc(nsize)))
|
||||
return false;
|
||||
|
||||
if (list->items) {
|
||||
memcpy(tmp, list->items, sizeof(struct bm_item*) * list->allocated);
|
||||
free(list->items);
|
||||
}
|
||||
}
|
||||
if (!(tmp = realloc(list->items, nsize)))
|
||||
return false;
|
||||
|
||||
list->items = tmp;
|
||||
list->allocated += step;
|
||||
|
@ -136,9 +129,18 @@ list_remove_item_at(struct list *list, uint32_t index)
|
|||
bool
|
||||
list_remove_item(struct list *list, const void *item)
|
||||
{
|
||||
assert(list && item);
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < list->count && list->items[i] != item; ++i);
|
||||
return list_remove_item_at(list, i);
|
||||
}
|
||||
|
||||
void
|
||||
list_sort(struct list *list, int (*compar)(const void *a, const void *b))
|
||||
{
|
||||
assert(list && compar);
|
||||
qsort(list->items, list->count, sizeof(void*), compar);
|
||||
}
|
||||
|
||||
/* vim: set ts=8 sw=4 tw=0 :*/
|
||||
|
|
19
lib/menu.c
19
lib/menu.c
|
@ -25,7 +25,7 @@ bm_menu_item_is_selected(const struct bm_menu *menu, const struct bm_item *item)
|
|||
}
|
||||
|
||||
struct bm_menu*
|
||||
bm_menu_new(const char *renderer)
|
||||
bm_menu_new(const char *renderer, enum bm_prioritory prioritory)
|
||||
{
|
||||
struct bm_menu *menu;
|
||||
if (!(menu = calloc(1, sizeof(struct bm_menu))))
|
||||
|
@ -34,19 +34,24 @@ bm_menu_new(const char *renderer)
|
|||
uint32_t count;
|
||||
const struct bm_renderer **renderers = bm_get_renderers(&count);
|
||||
|
||||
bool status = false;
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
if (prioritory != BM_PRIO_ANY && renderers[i]->api.prioritory != prioritory)
|
||||
continue;
|
||||
|
||||
if (renderer && strcmp(renderer, renderers[i]->name))
|
||||
continue;
|
||||
|
||||
if (bm_renderer_activate((struct bm_renderer*)renderers[i], menu)) {
|
||||
status = true;
|
||||
menu->renderer = renderers[i];
|
||||
break;
|
||||
if (renderers[i]->api.prioritory == BM_PRIO_TERMINAL) {
|
||||
const char *term = getenv("TERM");
|
||||
if (!term || !strlen(term))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bm_renderer_activate((struct bm_renderer*)renderers[i], menu))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
if (!menu->renderer) {
|
||||
bm_menu_free(menu);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
#ifndef _BM_CAIRO_H_
|
||||
#define _BM_CAIRO_H_
|
||||
|
||||
#include "internal.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
struct cairo {
|
||||
cairo_t *cr;
|
||||
cairo_surface_t *surface;
|
||||
};
|
||||
|
||||
struct cairo_color {
|
||||
float r, g, b, a;
|
||||
};
|
||||
|
||||
struct cairo_font {
|
||||
const char *name;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct cairo_paint {
|
||||
struct cairo_color color;
|
||||
struct cairo_font font;
|
||||
};
|
||||
|
||||
struct cairo_result {
|
||||
cairo_text_extents_t te;
|
||||
};
|
||||
|
||||
__attribute__((unused)) BM_LOG_ATTR(6, 7) static bool
|
||||
bm_cairo_draw_line(struct cairo *cairo, struct cairo_paint *paint, struct cairo_result *result, int32_t x, int32_t y, const char *fmt, ...)
|
||||
{
|
||||
static size_t blen = 0;
|
||||
static char *buffer = NULL;
|
||||
assert(cairo && paint && result && fmt);
|
||||
memset(result, 0, sizeof(struct cairo_result));
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool ret = bm_vrprintf(&buffer, &blen, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
cairo_set_source_rgba(cairo->cr, paint->color.r, paint->color.b, paint->color.g, paint->color.a);
|
||||
|
||||
cairo_text_extents_t te;
|
||||
cairo_select_font_face(cairo->cr, paint->font.name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
|
||||
cairo_set_font_size(cairo->cr, paint->font.size);
|
||||
cairo_text_extents(cairo->cr, buffer, &te);
|
||||
cairo_move_to(cairo->cr, x, y * te.height + te.height);
|
||||
cairo_show_text(cairo->cr, buffer);
|
||||
|
||||
memcpy(&result->te, &te, sizeof(te));
|
||||
return true;
|
||||
}
|
||||
|
||||
__attribute__((unused)) static void
|
||||
bm_cairo_paint(struct cairo *cairo, uint32_t width, uint32_t height, const struct bm_menu *menu)
|
||||
{
|
||||
cairo_set_source_rgb(cairo->cr, 18.0f / 255.0f, 18 / 255.0f, 18.0f / 255.0f);
|
||||
cairo_rectangle(cairo->cr, 0, 0, width, height);
|
||||
cairo_fill(cairo->cr);
|
||||
|
||||
struct cairo_result result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
|
||||
struct cairo_paint paint;
|
||||
memset(&paint, 0, sizeof(paint));
|
||||
paint.font.name = "Terminus";
|
||||
paint.font.size = 12;
|
||||
paint.color = (struct cairo_color){ 1.0, 0.0, 0.0, 1.0 };
|
||||
|
||||
if (menu->title)
|
||||
bm_cairo_draw_line(cairo, &paint, &result, result.te.x_advance, 0, "%s ", menu->title);
|
||||
if (menu->filter)
|
||||
bm_cairo_draw_line(cairo, &paint, &result, result.te.x_advance, 0, "%s", menu->filter);
|
||||
|
||||
uint32_t count, cl = 1;
|
||||
uint32_t lines = height / paint.font.size;
|
||||
struct bm_item **items = bm_menu_get_filtered_items(menu, &count);
|
||||
for (uint32_t i = (menu->index / (lines - 1)) * (lines - 1); i < count && cl < lines; ++i) {
|
||||
bool highlighted = (items[i] == bm_menu_get_highlighted_item(menu));
|
||||
|
||||
if (highlighted)
|
||||
paint.color = (struct cairo_color){ 1.0, 0.0, 0.0, 1.0 };
|
||||
else if (bm_menu_item_is_selected(menu, items[i]))
|
||||
paint.color = (struct cairo_color){ 1.0, 0.0, 0.0, 1.0 };
|
||||
else
|
||||
paint.color = (struct cairo_color){ 1.0, 1.0, 1.0, 1.0 };
|
||||
|
||||
bm_cairo_draw_line(cairo, &paint, &result, 0, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : ""));
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _BM_CAIRO_H */
|
||||
|
||||
/* vim: set ts=8 sw=4 tw=0 :*/
|
|
@ -1,25 +1,20 @@
|
|||
#if __APPLE__
|
||||
# define _C99_SOURCE
|
||||
# include <stdio.h> /* vsnprintf */
|
||||
# undef _C99_SOURCE
|
||||
#endif
|
||||
|
||||
#define _XOPEN_SOURCE 500
|
||||
#include <signal.h> /* sigaction */
|
||||
#include <stdarg.h> /* vsnprintf */
|
||||
#undef _XOPEN_SOURCE
|
||||
#define _DEFAULT_SOURCE
|
||||
#define _XOPEN_SOURCE_EXTENDED
|
||||
|
||||
#include "internal.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <wchar.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <locale.h>
|
||||
#include <ncurses.h>
|
||||
#include <dlfcn.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
#if _WIN32
|
||||
static const char *TTY = "CON";
|
||||
#else
|
||||
|
@ -61,7 +56,8 @@ crash_handler(int sig)
|
|||
terminate();
|
||||
}
|
||||
|
||||
static void resize_handler(int sig)
|
||||
static void
|
||||
resize_handler(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
if (!curses.stdscr)
|
||||
|
@ -71,56 +67,26 @@ static void resize_handler(int sig)
|
|||
refresh();
|
||||
}
|
||||
|
||||
static bool
|
||||
resize_buffer(char **buffer, size_t *osize, size_t nsize)
|
||||
{
|
||||
assert(buffer);
|
||||
assert(osize);
|
||||
|
||||
if (nsize == 0 || nsize <= *osize)
|
||||
return false;
|
||||
|
||||
void *tmp;
|
||||
if (!*buffer || !(tmp = realloc(*buffer, nsize))) {
|
||||
if (!(tmp = malloc(nsize)))
|
||||
return 0;
|
||||
|
||||
if (*buffer) {
|
||||
memcpy(tmp, *buffer, *osize);
|
||||
free(*buffer);
|
||||
}
|
||||
}
|
||||
|
||||
*buffer = tmp;
|
||||
*osize = nsize;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if __GNUC__
|
||||
__attribute__((format(printf, 3, 4)))
|
||||
#endif
|
||||
static void
|
||||
draw_line(int32_t pair, int32_t y, const char *format, ...)
|
||||
BM_LOG_ATTR(3, 4) static void
|
||||
draw_line(int32_t pair, int32_t y, const char *fmt, ...)
|
||||
{
|
||||
static size_t blen = 0;
|
||||
static char *buffer = NULL;
|
||||
assert(fmt);
|
||||
|
||||
size_t ncols;
|
||||
if ((ncols = getmaxx(curses.stdscr)) <= 0)
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
size_t nlen = vsnprintf(NULL, 0, format, args);
|
||||
va_start(args, fmt);
|
||||
bool ret = bm_vrprintf(&buffer, &blen, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if ((!buffer || nlen > blen) && !resize_buffer(&buffer, &blen, nlen + 1))
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(buffer, blen, format, args);
|
||||
va_end(args);
|
||||
|
||||
size_t nlen = strlen(buffer);
|
||||
size_t dw = 0, i = 0;
|
||||
while (dw < ncols && i < nlen) {
|
||||
if (buffer[i] == '\t') buffer[i] = ' ';
|
||||
|
@ -132,7 +98,7 @@ draw_line(int32_t pair, int32_t y, const char *format, ...)
|
|||
if (dw < ncols) {
|
||||
/* line is too short, widen it */
|
||||
size_t offset = i + (ncols - dw);
|
||||
if (blen <= offset && !resize_buffer(&buffer, &blen, offset + 1))
|
||||
if (blen <= offset && !bm_resize_buffer(&buffer, &blen, offset + 1))
|
||||
return;
|
||||
|
||||
memset(buffer + nlen, ' ', offset - nlen);
|
||||
|
@ -145,7 +111,7 @@ draw_line(int32_t pair, int32_t y, const char *format, ...)
|
|||
size_t offset = i - (dw - ncols) + (ncols - cc) + 1;
|
||||
if (blen <= offset) {
|
||||
int32_t diff = offset - blen + 1;
|
||||
if (!resize_buffer(&buffer, &blen, blen + diff))
|
||||
if (!bm_resize_buffer(&buffer, &blen, blen + diff))
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -353,6 +319,8 @@ static bool
|
|||
constructor(struct bm_menu *menu)
|
||||
{
|
||||
(void)menu;
|
||||
assert(!curses.stdscr && "bemenu supports only one curses instance");
|
||||
|
||||
memset(&curses, 0, sizeof(curses));
|
||||
|
||||
struct sigaction action;
|
||||
|
@ -374,6 +342,8 @@ register_renderer(struct render_api *api)
|
|||
api->get_displayed_count = get_displayed_count;
|
||||
api->poll_key = poll_key;
|
||||
api->render = render;
|
||||
api->prioritory = BM_PRIO_TERMINAL;
|
||||
api->version = BM_VERSION;
|
||||
return "curses";
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,10 @@ FIND_PACKAGE(Wayland)
|
|||
if (WAYLAND_FOUND)
|
||||
FIND_PACKAGE(Cairo REQUIRED)
|
||||
FIND_PACKAGE(XKBCommon REQUIRED)
|
||||
INCLUDE_DIRECTORIES(${WAYLAND_CLIENT_INCLUDE_DIR} ${XKBCOMMON_INCLUDE_DIR} ${CAIRO_INCLUDE_DIRECTORIES})
|
||||
ADD_LIBRARY(bemenu-renderer-wayland SHARED wayland.c)
|
||||
INCLUDE(Wayland)
|
||||
WAYLAND_ADD_PROTOCOL_CLIENT(proto-xdg-shell "xdg-shell.xml" xdg-shell)
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${WAYLAND_CLIENT_INCLUDE_DIR} ${XKBCOMMON_INCLUDE_DIR} ${CAIRO_INCLUDE_DIRECTORIES})
|
||||
ADD_LIBRARY(bemenu-renderer-wayland SHARED wayland.c registry.c window.c ${proto-xdg-shell})
|
||||
SET_TARGET_PROPERTIES(bemenu-renderer-wayland PROPERTIES PREFIX "")
|
||||
TARGET_LINK_LIBRARIES(bemenu-renderer-wayland ${WAYLAND_CLIENT_LIBRARIES} ${XKBCOMMON_LIBRARIES} ${CAIRO_LIBRARIES})
|
||||
INSTALL(TARGETS bemenu-renderer-wayland DESTINATION lib/bemenu)
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
#include "wayland.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
const char *BM_XKB_MASK_NAMES[MASK_LAST] = {
|
||||
XKB_MOD_NAME_SHIFT,
|
||||
XKB_MOD_NAME_CAPS,
|
||||
XKB_MOD_NAME_CTRL,
|
||||
XKB_MOD_NAME_ALT,
|
||||
"Mod2",
|
||||
"Mod3",
|
||||
XKB_MOD_NAME_LOGO,
|
||||
"Mod5",
|
||||
};
|
||||
|
||||
const enum mod_bit BM_XKB_MODS[MASK_LAST] = {
|
||||
MOD_SHIFT,
|
||||
MOD_CAPS,
|
||||
MOD_CTRL,
|
||||
MOD_ALT,
|
||||
MOD_MOD2,
|
||||
MOD_MOD3,
|
||||
MOD_LOGO,
|
||||
MOD_MOD5
|
||||
};
|
||||
|
||||
static void
|
||||
xdg_shell_ping(void *data, struct xdg_shell *shell, uint32_t serial)
|
||||
{
|
||||
(void)data;
|
||||
xdg_shell_pong(shell, serial);
|
||||
}
|
||||
|
||||
static const struct xdg_shell_listener xdg_shell_listener = {
|
||||
.ping = xdg_shell_ping,
|
||||
};
|
||||
|
||||
static void
|
||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
{
|
||||
(void)wl_shm;
|
||||
struct wayland *wayland = data;
|
||||
wayland->formats |= (1 << format);
|
||||
}
|
||||
|
||||
struct wl_shm_listener shm_listener = {
|
||||
.format = shm_format
|
||||
};
|
||||
|
||||
static void
|
||||
keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size)
|
||||
{
|
||||
(void)keyboard;
|
||||
struct input *input = data;
|
||||
|
||||
if (!data) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
char *map_str;
|
||||
if ((map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
struct xkb_keymap *keymap = xkb_keymap_new_from_string(input->xkb.context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
|
||||
munmap(map_str, size);
|
||||
close(fd);
|
||||
|
||||
if (!keymap) {
|
||||
fprintf(stderr, "failed to compile keymap\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct xkb_state *state;
|
||||
if (!(state = xkb_state_new(keymap))) {
|
||||
fprintf(stderr, "failed to create XKB state\n");
|
||||
xkb_keymap_unref(keymap);
|
||||
return;
|
||||
}
|
||||
|
||||
xkb_keymap_unref(input->xkb.keymap);
|
||||
xkb_state_unref(input->xkb.state);
|
||||
input->xkb.keymap = keymap;
|
||||
input->xkb.state = state;
|
||||
|
||||
for (uint32_t i = 0; i < MASK_LAST; ++i)
|
||||
input->xkb.masks[i] = xkb_keymap_mod_get_index(input->xkb.keymap, BM_XKB_MASK_NAMES[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys)
|
||||
{
|
||||
(void)data, (void)keyboard, (void)serial, (void)surface, (void)keys;
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface)
|
||||
{
|
||||
(void)data, (void)keyboard, (void)serial, (void)surface;
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w)
|
||||
{
|
||||
(void)keyboard, (void)serial, (void)time;
|
||||
struct input *input = data;
|
||||
enum wl_keyboard_key_state state = state_w;
|
||||
|
||||
if (!input->xkb.state)
|
||||
return;
|
||||
|
||||
uint32_t code = key + 8;
|
||||
xkb_keysym_t sym = xkb_state_key_get_one_sym(input->xkb.state, code);
|
||||
|
||||
input->sym = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? sym : XKB_KEY_NoSymbol);
|
||||
input->code = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? code : 0);
|
||||
|
||||
if (input->notify.key)
|
||||
input->notify.key(state, sym, code);
|
||||
|
||||
#if 0
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED &&
|
||||
key == input->repeat_key) {
|
||||
its.it_interval.tv_sec = 0;
|
||||
its.it_interval.tv_nsec = 0;
|
||||
its.it_value.tv_sec = 0;
|
||||
its.it_value.tv_nsec = 0;
|
||||
timerfd_settime(input->repeat_timer_fd, 0, &its, NULL);
|
||||
} else if (state == WL_KEYBOARD_KEY_STATE_PRESSED &&
|
||||
xkb_keymap_key_repeats(input->xkb.keymap, code)) {
|
||||
input->repeat_sym = sym;
|
||||
input->repeat_key = key;
|
||||
input->repeat_time = time;
|
||||
its.it_interval.tv_sec = input->repeat_rate_sec;
|
||||
its.it_interval.tv_nsec = input->repeat_rate_nsec;
|
||||
its.it_value.tv_sec = input->repeat_delay_sec;
|
||||
its.it_value.tv_nsec = input->repeat_delay_nsec;
|
||||
timerfd_settime(input->repeat_timer_fd, 0, &its, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_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)
|
||||
{
|
||||
(void)keyboard, (void)serial;
|
||||
struct input *input = data;
|
||||
|
||||
if (!input->xkb.keymap)
|
||||
return;
|
||||
|
||||
xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
||||
xkb_mod_mask_t mask = xkb_state_serialize_mods(input->xkb.state, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
|
||||
|
||||
input->modifiers = 0;
|
||||
for (uint32_t i = 0; i < MASK_LAST; ++i) {
|
||||
if (mask & input->xkb.masks[i])
|
||||
input->modifiers |= BM_XKB_MODS[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay)
|
||||
{
|
||||
(void)data, (void)keyboard, (void)rate, (void)delay;
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener keyboard_listener = {
|
||||
.keymap = keyboard_handle_keymap,
|
||||
.enter = keyboard_handle_enter,
|
||||
.leave = keyboard_handle_leave,
|
||||
.key = keyboard_handle_key,
|
||||
.modifiers = keyboard_handle_modifiers,
|
||||
.repeat_info = keyboard_handle_repeat_info
|
||||
};
|
||||
|
||||
static void
|
||||
seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps)
|
||||
{
|
||||
struct input *input = data;
|
||||
|
||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
|
||||
input->keyboard = wl_seat_get_keyboard(seat);
|
||||
wl_keyboard_add_listener(input->keyboard, &keyboard_listener, data);
|
||||
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
|
||||
wl_keyboard_destroy(input->keyboard);
|
||||
input->keyboard = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
seat_handle_name(void *data, struct wl_seat *seat, const char *name)
|
||||
{
|
||||
(void)data, (void)seat, (void)name;
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
seat_handle_capabilities,
|
||||
seat_handle_name
|
||||
};
|
||||
|
||||
static void
|
||||
registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version)
|
||||
{
|
||||
(void)version;
|
||||
struct wayland *wayland = data;
|
||||
|
||||
if (strcmp(interface, "wl_compositor") == 0) {
|
||||
wayland->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1);
|
||||
} else if (strcmp(interface, "xdg_shell") == 0) {
|
||||
wayland->xdg_shell = wl_registry_bind(registry, id, &xdg_shell_interface, 1);
|
||||
xdg_shell_use_unstable_version(wayland->xdg_shell, 4);
|
||||
xdg_shell_add_listener(wayland->xdg_shell, &xdg_shell_listener, data);
|
||||
} else if (strcmp(interface, "wl_shell") == 0) {
|
||||
wayland->shell = wl_registry_bind(registry, id, &wl_shell_interface, 1);
|
||||
} else if (strcmp(interface, "wl_seat") == 0) {
|
||||
wayland->seat = wl_registry_bind(registry, id, &wl_seat_interface, XDG_SHELL_VERSION_CURRENT);
|
||||
wl_seat_add_listener(wayland->seat, &seat_listener, &wayland->input);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
wayland->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
|
||||
wl_shm_add_listener(wayland->shm, &shm_listener, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
|
||||
{
|
||||
(void)data, (void)registry, (void)name;
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
registry_handle_global,
|
||||
registry_handle_global_remove
|
||||
};
|
||||
|
||||
void
|
||||
bm_wl_registry_destroy(struct wayland *wayland)
|
||||
{
|
||||
assert(wayland);
|
||||
|
||||
if (wayland->shm)
|
||||
wl_shm_destroy(wayland->shm);
|
||||
|
||||
if (wayland->shell)
|
||||
wl_shell_destroy(wayland->shell);
|
||||
|
||||
if (wayland->xdg_shell)
|
||||
xdg_shell_destroy(wayland->xdg_shell);
|
||||
|
||||
if (wayland->compositor)
|
||||
wl_compositor_destroy(wayland->compositor);
|
||||
|
||||
if (wayland->registry)
|
||||
wl_registry_destroy(wayland->registry);
|
||||
}
|
||||
|
||||
bool
|
||||
bm_wl_registry_register(struct wayland *wayland)
|
||||
{
|
||||
assert(wayland);
|
||||
|
||||
if (!(wayland->registry = wl_display_get_registry(wayland->display)))
|
||||
return false;
|
||||
|
||||
wl_registry_add_listener(wayland->registry, ®istry_listener, wayland);
|
||||
wl_display_roundtrip(wayland->display); // trip 1, registry globals
|
||||
if (!wayland->compositor || !wayland->seat || !wayland->shm || !(wayland->shell || wayland->xdg_shell))
|
||||
return false;
|
||||
|
||||
wl_display_roundtrip(wayland->display); // trip 2, global listeners
|
||||
if (!wayland->input.keyboard || !(wayland->formats & (1 << WL_SHM_FORMAT_ARGB8888)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* vim: set ts=8 sw=4 tw=0 :*/
|
|
@ -1,575 +1,33 @@
|
|||
#define _DEFAULT_SOURCE
|
||||
#include "internal.h"
|
||||
#include "version.h"
|
||||
#include "wayland.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#define MOD_SHIFT_MASK 0x01
|
||||
#define MOD_ALT_MASK 0x02
|
||||
#define MOD_CONTROL_MASK 0x04
|
||||
|
||||
struct xkb {
|
||||
struct xkb_context *context;
|
||||
struct xkb_keymap *keymap;
|
||||
struct xkb_state *state;
|
||||
xkb_mod_mask_t control_mask;
|
||||
xkb_mod_mask_t alt_mask;
|
||||
xkb_mod_mask_t shift_mask;
|
||||
xkb_keysym_t sym;
|
||||
uint32_t key;
|
||||
uint32_t modifiers;
|
||||
};
|
||||
|
||||
struct cairo {
|
||||
cairo_t *cr;
|
||||
cairo_surface_t *surface;
|
||||
};
|
||||
|
||||
struct buffer {
|
||||
struct cairo cairo;
|
||||
struct wl_buffer *buffer;
|
||||
int busy;
|
||||
};
|
||||
|
||||
static struct wayland {
|
||||
struct bm_menu *menu;
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_surface *surface;
|
||||
struct wl_shell_surface *shell_surface;
|
||||
struct wl_seat *seat;
|
||||
struct wl_keyboard *keyboard;
|
||||
struct wl_shm *shm;
|
||||
#if 0
|
||||
struct xdg_shell *shell;
|
||||
#else
|
||||
struct wl_shell *shell;
|
||||
#endif
|
||||
struct xkb xkb;
|
||||
struct buffer buffers[2];
|
||||
uint32_t width, height;
|
||||
uint32_t formats;
|
||||
} wayland;
|
||||
|
||||
// XXX: move to utils.c if reused
|
||||
static char*
|
||||
csprintf(const char *fmt, ...)
|
||||
{
|
||||
assert(fmt);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
size_t len = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
char *buffer;
|
||||
if (!(buffer = calloc(1, len)))
|
||||
return NULL;
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer, len, fmt, args);
|
||||
va_end(args);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static int
|
||||
set_cloexec_or_close(int fd)
|
||||
{
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
long flags = fcntl(fd, F_GETFD);
|
||||
if (flags == -1)
|
||||
goto err;
|
||||
|
||||
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
||||
goto err;
|
||||
|
||||
return fd;
|
||||
|
||||
err:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
create_tmpfile_cloexec(char *tmpname)
|
||||
{
|
||||
int fd;
|
||||
|
||||
#ifdef HAVE_MKOSTEMP
|
||||
if ((fd = mkostemp(tmpname, O_CLOEXEC)) >= 0)
|
||||
unlink(tmpname);
|
||||
#else
|
||||
if ((fd = mkstemp(tmpname)) >= 0) {
|
||||
fd = set_cloexec_or_close(fd);
|
||||
unlink(tmpname);
|
||||
}
|
||||
#endif
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int
|
||||
os_create_anonymous_file(off_t size)
|
||||
{
|
||||
static const char template[] = "/bemenu-shared-XXXXXX";
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
const char *path;
|
||||
if (!(path = getenv("XDG_RUNTIME_DIR")) || strlen(path) <= 0) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *name;
|
||||
int ts = (path[strlen(path) - 1] == '/');
|
||||
if (!(name = csprintf("%s%s%s", path, (ts ? "" : "/"), template)))
|
||||
return -1;
|
||||
|
||||
fd = create_tmpfile_cloexec(name);
|
||||
free(name);
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
#ifdef HAVE_POSIX_FALLOCATE
|
||||
if ((ret = posix_fallocate(fd, 0, size)) != 0) {
|
||||
close(fd);
|
||||
errno = ret;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
if ((ret = ftruncate(fd, size)) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_release(void *data, struct wl_buffer *buffer)
|
||||
{
|
||||
struct buffer *mybuf = data;
|
||||
mybuf->busy = 0;
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener buffer_listener = {
|
||||
buffer_release
|
||||
};
|
||||
|
||||
static bool
|
||||
create_shm_buffer(struct buffer *buffer, int32_t width, int32_t height, uint32_t format)
|
||||
{
|
||||
uint32_t stride = width * 4;
|
||||
uint32_t size = stride * height;
|
||||
|
||||
int fd;
|
||||
if ((fd = os_create_anonymous_file(size)) < 0) {
|
||||
fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
fprintf(stderr, "mmap failed: %m\n");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: error handling
|
||||
struct wl_shm_pool *pool = wl_shm_create_pool(wayland.shm, fd, size);
|
||||
buffer->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format);
|
||||
wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
|
||||
wl_shm_pool_destroy(pool);
|
||||
close(fd);
|
||||
|
||||
buffer->cairo.surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, stride);
|
||||
buffer->cairo.cr = cairo_create(buffer->cairo.surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct buffer*
|
||||
next_buffer(void)
|
||||
{
|
||||
struct buffer *buffer;
|
||||
|
||||
if (!wayland.buffers[0].busy)
|
||||
buffer = &wayland.buffers[0];
|
||||
else if (!wayland.buffers[1].busy)
|
||||
buffer = &wayland.buffers[1];
|
||||
else
|
||||
return NULL;
|
||||
|
||||
if (!buffer->buffer) {
|
||||
if (!create_shm_buffer(buffer, wayland.width, wayland.height, WL_SHM_FORMAT_ARGB8888))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static bool
|
||||
resize_buffer(char **buffer, size_t *osize, size_t nsize)
|
||||
{
|
||||
assert(buffer);
|
||||
assert(osize);
|
||||
|
||||
if (nsize == 0 || nsize <= *osize)
|
||||
return false;
|
||||
|
||||
void *tmp;
|
||||
if (!*buffer || !(tmp = realloc(*buffer, nsize))) {
|
||||
if (!(tmp = malloc(nsize)))
|
||||
return 0;
|
||||
|
||||
if (*buffer) {
|
||||
memcpy(tmp, *buffer, *osize);
|
||||
free(*buffer);
|
||||
}
|
||||
}
|
||||
|
||||
*buffer = tmp;
|
||||
*osize = nsize;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if __GNUC__
|
||||
__attribute__((format(printf, 5, 6)))
|
||||
#endif
|
||||
static int32_t
|
||||
draw_line(struct cairo *c, int32_t pair, int32_t x, int32_t y, const char *format, ...)
|
||||
{
|
||||
static size_t blen = 0;
|
||||
static char *buffer = NULL;
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
size_t nlen = vsnprintf(NULL, 0, format, args);
|
||||
va_end(args);
|
||||
|
||||
if ((!buffer || nlen > blen) && !resize_buffer(&buffer, &blen, nlen + 1))
|
||||
return;
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(buffer, blen, format, args);
|
||||
va_end(args);
|
||||
|
||||
cairo_text_extents_t te;
|
||||
switch (pair) {
|
||||
case 2:
|
||||
cairo_set_source_rgb(c->cr, 1.0, 0.0, 0.0);
|
||||
break;
|
||||
case 1:
|
||||
cairo_set_source_rgb(c->cr, 1.0, 0.0, 0.0);
|
||||
break;
|
||||
case 0:
|
||||
cairo_set_source_rgb(c->cr, 1.0, 1.0, 1.0);
|
||||
break;
|
||||
};
|
||||
|
||||
cairo_select_font_face(c->cr, "Terminus", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
|
||||
cairo_set_font_size(c->cr, 12);
|
||||
cairo_text_extents(c->cr, buffer, &te);
|
||||
cairo_move_to(c->cr, x, y * te.height + te.height);
|
||||
cairo_show_text(c->cr, buffer);
|
||||
return x + te.x_advance;
|
||||
}
|
||||
|
||||
static void
|
||||
paint(struct cairo *c, int width, int height, uint32_t time)
|
||||
{
|
||||
cairo_set_source_rgb(c->cr, 18.0f / 255.0f, 18 / 255.0f, 18.0f / 255.0f);
|
||||
cairo_rectangle(c->cr, 0, 0, width, height);
|
||||
cairo_fill(c->cr);
|
||||
|
||||
struct bm_menu *menu = wayland.menu;
|
||||
|
||||
int32_t x = 0;
|
||||
if (menu->title)
|
||||
x = draw_line(c, 1, x, 0, "%s ", menu->title);
|
||||
if (menu->filter)
|
||||
x = draw_line(c, 1, x, 0, "%s", menu->filter);
|
||||
|
||||
uint32_t lines = height / 8;
|
||||
uint32_t count, cl = 1;
|
||||
struct bm_item **items = bm_menu_get_filtered_items(menu, &count);
|
||||
for (uint32_t i = (menu->index / (lines - 1)) * (lines - 1); i < count && cl < lines; ++i) {
|
||||
bool highlighted = (items[i] == bm_menu_get_highlighted_item(menu));
|
||||
int32_t color = (highlighted ? 2 : (bm_menu_item_is_selected(menu, items[i]) ? 1 : 0));
|
||||
draw_line(c, color, 0, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : ""));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
redraw(void *data, uint32_t time)
|
||||
{
|
||||
struct wayland *d = data;
|
||||
struct buffer *buffer;
|
||||
|
||||
if (!(buffer = next_buffer()))
|
||||
return;
|
||||
|
||||
paint(&buffer->cairo, d->width, d->height, time);
|
||||
wl_surface_attach(d->surface, buffer->buffer, 0, 0);
|
||||
wl_surface_commit(d->surface);
|
||||
buffer->busy = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
{
|
||||
(void)wl_shm;
|
||||
struct wayland *d = data;
|
||||
d->formats |= (1 << format);
|
||||
}
|
||||
|
||||
struct wl_shm_listener shm_listener = {
|
||||
.format = shm_format
|
||||
};
|
||||
|
||||
#if 0
|
||||
static void
|
||||
xdg_shell_ping(void *data, struct xdg_shell *shell, uint32_t serial)
|
||||
{
|
||||
(void)data;
|
||||
xdg_shell_pong(shell, serial);
|
||||
}
|
||||
|
||||
static const struct xdg_shell_listener xdg_shell_listener = {
|
||||
xdg_shell_ping,
|
||||
};
|
||||
#endif
|
||||
|
||||
static void
|
||||
keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size)
|
||||
{
|
||||
struct wayland *d = data;
|
||||
|
||||
if (!data) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
char *map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (map_str == MAP_FAILED) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
struct xkb_keymap *keymap = xkb_keymap_new_from_string(d->xkb.context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
|
||||
munmap(map_str, size);
|
||||
close(fd);
|
||||
|
||||
if (!keymap) {
|
||||
fprintf(stderr, "failed to compile keymap\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct xkb_state *state = xkb_state_new(keymap);
|
||||
if (!state) {
|
||||
fprintf(stderr, "failed to create XKB state\n");
|
||||
xkb_keymap_unref(keymap);
|
||||
return;
|
||||
}
|
||||
|
||||
xkb_keymap_unref(d->xkb.keymap);
|
||||
xkb_state_unref(d->xkb.state);
|
||||
d->xkb.keymap = keymap;
|
||||
d->xkb.state = state;
|
||||
d->xkb.control_mask = 1 << xkb_keymap_mod_get_index(d->xkb.keymap, "Control");
|
||||
d->xkb.alt_mask = 1 << xkb_keymap_mod_get_index(d->xkb.keymap, "Mod1");
|
||||
d->xkb.shift_mask = 1 << xkb_keymap_mod_get_index(d->xkb.keymap, "Shift");
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys)
|
||||
{
|
||||
(void)data, (void)keyboard, (void)serial, (void)surface, (void)keys;
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface)
|
||||
{
|
||||
(void)data, (void)keyboard, (void)serial, (void)surface;
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w)
|
||||
{
|
||||
struct wayland *d = data;
|
||||
enum wl_keyboard_key_state state = state_w;
|
||||
|
||||
if (!d->xkb.state)
|
||||
return;
|
||||
|
||||
uint32_t code = key + 8;
|
||||
xkb_keysym_t sym = xkb_state_key_get_one_sym(d->xkb.state, code);
|
||||
|
||||
d->xkb.key = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? code : 0);
|
||||
d->xkb.sym = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? sym : XKB_KEY_NoSymbol);
|
||||
|
||||
#if 0
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED &&
|
||||
key == input->repeat_key) {
|
||||
its.it_interval.tv_sec = 0;
|
||||
its.it_interval.tv_nsec = 0;
|
||||
its.it_value.tv_sec = 0;
|
||||
its.it_value.tv_nsec = 0;
|
||||
timerfd_settime(input->repeat_timer_fd, 0, &its, NULL);
|
||||
} else if (state == WL_KEYBOARD_KEY_STATE_PRESSED &&
|
||||
xkb_keymap_key_repeats(input->xkb.keymap, code)) {
|
||||
input->repeat_sym = sym;
|
||||
input->repeat_key = key;
|
||||
input->repeat_time = time;
|
||||
its.it_interval.tv_sec = input->repeat_rate_sec;
|
||||
its.it_interval.tv_nsec = input->repeat_rate_nsec;
|
||||
its.it_value.tv_sec = input->repeat_delay_sec;
|
||||
its.it_value.tv_nsec = input->repeat_delay_nsec;
|
||||
timerfd_settime(input->repeat_timer_fd, 0, &its, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_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)
|
||||
{
|
||||
(void)keyboard, (void)serial;
|
||||
struct wayland *d = data;
|
||||
|
||||
if (!d->xkb.keymap)
|
||||
return;
|
||||
|
||||
xkb_state_update_mask(d->xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
||||
xkb_mod_mask_t mask = xkb_state_serialize_mods(d->xkb.state, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
|
||||
|
||||
d->xkb.modifiers = 0;
|
||||
if (mask & d->xkb.control_mask)
|
||||
d->xkb.modifiers |= MOD_CONTROL_MASK;
|
||||
if (mask & d->xkb.alt_mask)
|
||||
d->xkb.modifiers |= MOD_ALT_MASK;
|
||||
if (mask & d->xkb.shift_mask)
|
||||
d->xkb.modifiers |= MOD_SHIFT_MASK;
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay)
|
||||
{
|
||||
(void)data, (void)keyboard, (void)rate, (void)delay;
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener keyboard_listener = {
|
||||
.keymap = keyboard_handle_keymap,
|
||||
.enter = keyboard_handle_enter,
|
||||
.leave = keyboard_handle_leave,
|
||||
.key = keyboard_handle_key,
|
||||
.modifiers = keyboard_handle_modifiers,
|
||||
.repeat_info = keyboard_handle_repeat_info
|
||||
};
|
||||
|
||||
static void
|
||||
seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps)
|
||||
{
|
||||
struct wayland *d = data;
|
||||
|
||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !d->keyboard) {
|
||||
d->keyboard = wl_seat_get_keyboard(seat);
|
||||
wl_keyboard_add_listener(d->keyboard, &keyboard_listener, data);
|
||||
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && d->keyboard) {
|
||||
wl_keyboard_destroy(d->keyboard);
|
||||
d->keyboard = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
seat_handle_name(void *data, struct wl_seat *seat, const char *name)
|
||||
{
|
||||
(void)data, (void)seat, (void)name;
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
seat_handle_capabilities,
|
||||
seat_handle_name
|
||||
};
|
||||
|
||||
static void
|
||||
registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version)
|
||||
{
|
||||
(void)version;
|
||||
struct wayland *d = data;
|
||||
|
||||
if (strcmp(interface, "wl_compositor") == 0) {
|
||||
d->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1);
|
||||
#if 0
|
||||
} else if (strcmp(interface, "xdg_shell") == 0) {
|
||||
d->shell = wl_registry_bind(registry, id, &xdg_shell_interface, 1);
|
||||
xdg_shell_use_unstable_version(d->shell, XDG_VERSION);
|
||||
xdg_shell_add_listener(d->shell, &xdg_shell_listener, d);
|
||||
#else
|
||||
} else if (strcmp(interface, "wl_shell") == 0) {
|
||||
d->shell = wl_registry_bind(registry, id, &wl_shell_interface, 1);
|
||||
#endif
|
||||
} else if (strcmp(interface, "wl_seat") == 0) {
|
||||
d->seat = wl_registry_bind(registry, id, &wl_seat_interface, 4);
|
||||
wl_seat_add_listener(d->seat, &seat_listener, d);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
|
||||
wl_shm_add_listener(d->shm, &shm_listener, d);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
|
||||
{
|
||||
(void)data, (void)registry, (void)name;
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
registry_handle_global,
|
||||
registry_handle_global_remove
|
||||
};
|
||||
|
||||
static void
|
||||
render(const struct bm_menu *menu)
|
||||
{
|
||||
(void)menu;
|
||||
redraw(&wayland, 0);
|
||||
wl_display_dispatch(wayland.display);
|
||||
struct wayland *wayland = menu->renderer->internal;
|
||||
bm_wl_window_render(&wayland->window, menu);
|
||||
wl_display_dispatch(wayland->display);
|
||||
}
|
||||
|
||||
static enum bm_key
|
||||
poll_key(const struct bm_menu *menu, unsigned int *unicode)
|
||||
{
|
||||
(void)menu;
|
||||
assert(unicode);
|
||||
struct wayland *wayland = menu->renderer->internal;
|
||||
assert(wayland && unicode);
|
||||
*unicode = 0;
|
||||
|
||||
if (wayland.xkb.sym == XKB_KEY_NoSymbol)
|
||||
if (wayland->input.sym == XKB_KEY_NoSymbol)
|
||||
return BM_KEY_UNICODE;
|
||||
|
||||
*unicode = xkb_state_key_get_utf32(wayland.xkb.state, wayland.xkb.key);
|
||||
*unicode = xkb_state_key_get_utf32(wayland->input.xkb.state, wayland->input.code);
|
||||
|
||||
switch (wayland.xkb.sym) {
|
||||
switch (wayland->input.sym) {
|
||||
case XKB_KEY_Up:
|
||||
return BM_KEY_UP;
|
||||
|
||||
|
@ -621,68 +79,55 @@ poll_key(const struct bm_menu *menu, unsigned int *unicode)
|
|||
static uint32_t
|
||||
get_displayed_count(const struct bm_menu *menu)
|
||||
{
|
||||
(void)menu;
|
||||
return wayland.height / 8;
|
||||
struct wayland *wayland = menu->renderer->internal;
|
||||
assert(wayland);
|
||||
return wayland->window.height / 12;
|
||||
}
|
||||
|
||||
static void
|
||||
destructor(struct bm_menu *menu)
|
||||
{
|
||||
(void)menu;
|
||||
struct wayland *wayland = menu->renderer->internal;
|
||||
|
||||
if (wayland.shm)
|
||||
wl_shm_destroy(wayland.shm);
|
||||
if (!wayland)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
if (wayland.shell)
|
||||
xdg_shell_destroy(wayland.shell);
|
||||
#endif
|
||||
bm_wl_window_destroy(&wayland->window);
|
||||
bm_wl_registry_destroy(wayland);
|
||||
|
||||
if (wayland.compositor)
|
||||
wl_compositor_destroy(wayland.compositor);
|
||||
|
||||
if (wayland.registry)
|
||||
wl_registry_destroy(wayland.registry);
|
||||
|
||||
if (wayland.display) {
|
||||
wl_display_flush(wayland.display);
|
||||
wl_display_disconnect(wayland.display);
|
||||
if (wayland->display) {
|
||||
wl_display_flush(wayland->display);
|
||||
wl_display_disconnect(wayland->display);
|
||||
}
|
||||
|
||||
free(wayland);
|
||||
menu->renderer->internal = NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
constructor(struct bm_menu *menu)
|
||||
{
|
||||
(void)menu;
|
||||
memset(&wayland, 0, sizeof(wayland));
|
||||
|
||||
if (!(wayland.display = wl_display_connect(NULL)))
|
||||
struct wayland *wayland;
|
||||
if (!(menu->renderer->internal = wayland = calloc(1, sizeof(struct wayland))))
|
||||
goto fail;
|
||||
|
||||
if (!(wayland.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS)))
|
||||
if (!(wayland->display = wl_display_connect(NULL)))
|
||||
goto fail;
|
||||
|
||||
wayland.width = 800;
|
||||
wayland.height = 480;
|
||||
|
||||
wayland.registry = wl_display_get_registry(wayland.display);
|
||||
wl_registry_add_listener(wayland.registry, ®istry_listener, &wayland);
|
||||
wl_display_roundtrip(wayland.display); // shm bind
|
||||
if (!wayland.shm)
|
||||
if (!(wayland->input.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS)))
|
||||
goto fail;
|
||||
|
||||
wl_display_roundtrip(wayland.display); // shm formats
|
||||
if (!(wayland.formats & (1 << WL_SHM_FORMAT_ARGB8888)))
|
||||
if (!bm_wl_registry_register(wayland))
|
||||
goto fail;
|
||||
|
||||
if (!(wayland.surface = wl_compositor_create_surface(wayland.compositor)))
|
||||
struct wl_surface *surface;
|
||||
if (!(surface = wl_compositor_create_surface(wayland->compositor)))
|
||||
goto fail;
|
||||
|
||||
if (!(wayland.shell_surface = wl_shell_get_shell_surface(wayland.shell, wayland.surface)))
|
||||
if (!bm_wl_window_create(&wayland->window, wayland->shm, wayland->shell, wayland->xdg_shell, surface))
|
||||
goto fail;
|
||||
|
||||
wayland.menu = menu;
|
||||
wl_surface_damage(wayland.surface, 0, 0, wayland.width, wayland.height);
|
||||
wayland->window.notify.render = bm_cairo_paint;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
|
@ -698,6 +143,8 @@ register_renderer(struct render_api *api)
|
|||
api->get_displayed_count = get_displayed_count;
|
||||
api->poll_key = poll_key;
|
||||
api->render = render;
|
||||
api->prioritory = BM_PRIO_GUI;
|
||||
api->version = BM_VERSION;
|
||||
return "wayland";
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
#ifndef _BM_WAYLAND_H_
|
||||
#define _BM_WAYLAND_H_
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
#include "wayland-xdg-shell-client-protocol.h"
|
||||
|
||||
#include "renderers/cairo.h"
|
||||
|
||||
struct bm_menu;
|
||||
|
||||
enum mod_bit {
|
||||
MOD_SHIFT = 1<<0,
|
||||
MOD_CAPS = 1<<1,
|
||||
MOD_CTRL = 1<<2,
|
||||
MOD_ALT = 1<<3,
|
||||
MOD_MOD2 = 1<<4,
|
||||
MOD_MOD3 = 1<<5,
|
||||
MOD_LOGO = 1<<6,
|
||||
MOD_MOD5 = 1<<7,
|
||||
};
|
||||
|
||||
enum mask {
|
||||
MASK_SHIFT,
|
||||
MASK_CAPS,
|
||||
MASK_CTRL,
|
||||
MASK_ALT,
|
||||
MASK_MOD2,
|
||||
MASK_MOD3,
|
||||
MASK_LOGO,
|
||||
MASK_MOD5,
|
||||
MASK_LAST
|
||||
};
|
||||
|
||||
const char *BM_XKB_MASK_NAMES[MASK_LAST];
|
||||
const enum mod_bit BM_XKB_MODS[MASK_LAST];
|
||||
|
||||
struct xkb {
|
||||
struct xkb_state *state;
|
||||
struct xkb_context *context;
|
||||
struct xkb_keymap *keymap;
|
||||
xkb_mod_mask_t masks[MASK_LAST];
|
||||
};
|
||||
|
||||
struct input {
|
||||
struct wl_keyboard *keyboard;
|
||||
struct xkb xkb;
|
||||
|
||||
xkb_keysym_t sym;
|
||||
uint32_t code;
|
||||
uint32_t modifiers;
|
||||
|
||||
struct {
|
||||
void (*key)(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code);
|
||||
} notify;
|
||||
};
|
||||
|
||||
struct buffer {
|
||||
struct cairo cairo;
|
||||
struct wl_buffer *buffer;
|
||||
uint32_t width, height;
|
||||
bool busy;
|
||||
};
|
||||
|
||||
struct window {
|
||||
struct wl_surface *surface;
|
||||
struct wl_shell_surface *shell_surface;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct wl_shm *shm;
|
||||
struct buffer buffers[2];
|
||||
uint32_t width, height;
|
||||
|
||||
struct {
|
||||
void (*render)(struct cairo *cairo, uint32_t width, uint32_t height, const struct bm_menu *menu);
|
||||
} notify;
|
||||
};
|
||||
|
||||
struct wayland {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_seat *seat;
|
||||
struct xdg_shell *xdg_shell;
|
||||
struct wl_shell *shell;
|
||||
struct wl_shm *shm;
|
||||
struct input input;
|
||||
struct window window;
|
||||
uint32_t formats;
|
||||
};
|
||||
|
||||
bool bm_wl_registry_register(struct wayland *wayland);
|
||||
void bm_wl_registry_destroy(struct wayland *wayland);
|
||||
void bm_wl_window_render(struct window *window, const struct bm_menu *menu);
|
||||
bool bm_wl_window_create(struct window *window, struct wl_shm *shm, struct wl_shell *shell, struct xdg_shell *xdg_shell, struct wl_surface *surface);
|
||||
void bm_wl_window_destroy(struct window *window);
|
||||
|
||||
#endif /* _BM_WAYLAND_H_ */
|
||||
|
||||
/* vim: set ts=8 sw=4 tw=0 :*/
|
|
@ -0,0 +1,311 @@
|
|||
#define _DEFAULT_SOURCE
|
||||
#include "internal.h"
|
||||
#include "wayland.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define USE_XDG_SHELL false
|
||||
|
||||
static int
|
||||
set_cloexec_or_close(int fd)
|
||||
{
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
long flags = fcntl(fd, F_GETFD);
|
||||
if (flags == -1)
|
||||
goto err;
|
||||
|
||||
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
||||
goto err;
|
||||
|
||||
return fd;
|
||||
|
||||
err:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
create_tmpfile_cloexec(char *tmpname)
|
||||
{
|
||||
int fd;
|
||||
|
||||
#ifdef HAVE_MKOSTEMP
|
||||
if ((fd = mkostemp(tmpname, O_CLOEXEC)) >= 0)
|
||||
unlink(tmpname);
|
||||
#else
|
||||
if ((fd = mkstemp(tmpname)) >= 0) {
|
||||
fd = set_cloexec_or_close(fd);
|
||||
unlink(tmpname);
|
||||
}
|
||||
#endif
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int
|
||||
os_create_anonymous_file(off_t size)
|
||||
{
|
||||
static const char template[] = "/bemenu-shared-XXXXXX";
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
const char *path;
|
||||
if (!(path = getenv("XDG_RUNTIME_DIR")) || strlen(path) <= 0) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *name;
|
||||
int ts = (path[strlen(path) - 1] == '/');
|
||||
if (!(name = bm_dprintf("%s%s%s", path, (ts ? "" : "/"), template)))
|
||||
return -1;
|
||||
|
||||
fd = create_tmpfile_cloexec(name);
|
||||
free(name);
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
#ifdef HAVE_POSIX_FALLOCATE
|
||||
if ((ret = posix_fallocate(fd, 0, size)) != 0) {
|
||||
close(fd);
|
||||
errno = ret;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
if ((ret = ftruncate(fd, size)) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_release(void *data, struct wl_buffer *wl_buffer)
|
||||
{
|
||||
(void)wl_buffer;
|
||||
struct buffer *buffer = data;
|
||||
buffer->busy = false;
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener buffer_listener = {
|
||||
.release = buffer_release
|
||||
};
|
||||
|
||||
static void
|
||||
destroy_buffer(struct buffer *buffer)
|
||||
{
|
||||
if (buffer->buffer)
|
||||
wl_buffer_destroy(buffer->buffer);
|
||||
if (buffer->cairo.cr)
|
||||
cairo_destroy(buffer->cairo.cr);
|
||||
if (buffer->cairo.surface)
|
||||
cairo_surface_destroy(buffer->cairo.surface);
|
||||
|
||||
memset(buffer, 0, sizeof(struct buffer));
|
||||
}
|
||||
|
||||
static bool
|
||||
create_buffer(struct wl_shm *shm, struct buffer *buffer, int32_t width, int32_t height, uint32_t format)
|
||||
{
|
||||
int fd = -1;
|
||||
struct wl_shm_pool *pool = NULL;
|
||||
uint32_t stride = width * 4;
|
||||
uint32_t size = stride * height;
|
||||
|
||||
if ((fd = os_create_anonymous_file(size)) < 0) {
|
||||
fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
void *data;
|
||||
if ((data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
||||
fprintf(stderr, "mmap failed: %m\n");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(pool = wl_shm_create_pool(shm, fd, size))) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(buffer->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format)))
|
||||
goto fail;
|
||||
|
||||
wl_shm_pool_destroy(pool);
|
||||
pool = NULL;
|
||||
|
||||
close(fd);
|
||||
fd = -1;
|
||||
|
||||
wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
|
||||
|
||||
if (!(buffer->cairo.surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, stride)))
|
||||
goto fail;
|
||||
|
||||
if (!(buffer->cairo.cr = cairo_create(buffer->cairo.surface)))
|
||||
goto fail;
|
||||
|
||||
buffer->width = width;
|
||||
buffer->height = height;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
if (fd > -1)
|
||||
close(fd);
|
||||
if (pool)
|
||||
wl_shm_pool_destroy(pool);
|
||||
destroy_buffer(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct buffer*
|
||||
next_buffer(struct window *window)
|
||||
{
|
||||
assert(window);
|
||||
|
||||
struct buffer *buffer = NULL;
|
||||
for (int32_t i = 0; i < 2; ++i) {
|
||||
if (window->buffers[i].busy)
|
||||
continue;
|
||||
|
||||
buffer = &window->buffers[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
if (window->width != buffer->width || window->height != buffer->height)
|
||||
destroy_buffer(buffer);
|
||||
|
||||
if (!buffer->buffer && !create_buffer(window->shm, buffer, window->width, window->height, WL_SHM_FORMAT_ARGB8888))
|
||||
return NULL;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
resize(struct window *window, uint32_t width, uint32_t height)
|
||||
{
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_surface_ping(void *data, struct wl_shell_surface *surface, uint32_t serial)
|
||||
{
|
||||
(void)data;
|
||||
wl_shell_surface_pong(surface, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_surface_configure(void *data, struct wl_shell_surface *surface, uint32_t edges, int32_t width, int32_t height)
|
||||
{
|
||||
(void)surface, (void)edges;
|
||||
resize(data, width, height);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_surface_popup_done(void *data, struct wl_shell_surface *surface)
|
||||
{
|
||||
(void)data, (void)surface;
|
||||
}
|
||||
|
||||
static const struct wl_shell_surface_listener shell_surface_listener = {
|
||||
.ping = shell_surface_ping,
|
||||
.configure = shell_surface_configure,
|
||||
.popup_done = shell_surface_popup_done,
|
||||
};
|
||||
|
||||
static void
|
||||
xdg_surface_configure(void *data, struct xdg_surface *surface, int32_t width, int32_t height, struct wl_array *states, uint32_t serial)
|
||||
{
|
||||
(void)states;
|
||||
resize(data, width, height);
|
||||
xdg_surface_ack_configure(surface, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_surface_close(void *data, struct xdg_surface *surface)
|
||||
{
|
||||
(void)data, (void)surface;
|
||||
}
|
||||
|
||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
||||
.configure = xdg_surface_configure,
|
||||
.close = xdg_surface_close,
|
||||
};
|
||||
|
||||
void
|
||||
bm_wl_window_render(struct window *window, const struct bm_menu *menu)
|
||||
{
|
||||
assert(window && menu);
|
||||
|
||||
struct buffer *buffer;
|
||||
if (!(buffer = next_buffer(window)))
|
||||
return;
|
||||
|
||||
if (window->notify.render)
|
||||
window->notify.render(&buffer->cairo, buffer->width, buffer->height, menu);
|
||||
|
||||
wl_surface_damage(window->surface, 0, 0, buffer->width, buffer->height);
|
||||
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
|
||||
wl_surface_commit(window->surface);
|
||||
buffer->busy = true;
|
||||
}
|
||||
|
||||
void
|
||||
bm_wl_window_destroy(struct window *window)
|
||||
{
|
||||
assert(window);
|
||||
|
||||
for (int32_t i = 0; i < 2; ++i)
|
||||
destroy_buffer(&window->buffers[i]);
|
||||
|
||||
if (window->xdg_surface)
|
||||
xdg_surface_destroy(window->xdg_surface);
|
||||
|
||||
if (window->shell_surface)
|
||||
wl_shell_surface_destroy(window->shell_surface);
|
||||
|
||||
if (window->surface)
|
||||
wl_surface_destroy(window->surface);
|
||||
}
|
||||
|
||||
bool
|
||||
bm_wl_window_create(struct window *window, struct wl_shm *shm, struct wl_shell *shell, struct xdg_shell *xdg_shell, struct wl_surface *surface)
|
||||
{
|
||||
assert(window);
|
||||
|
||||
if (USE_XDG_SHELL && xdg_shell && (window->xdg_surface = xdg_shell_get_xdg_surface(xdg_shell, surface))) {
|
||||
xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window);
|
||||
xdg_surface_set_title(window->xdg_surface, "bemenu");
|
||||
} else if (shell && (window->shell_surface = wl_shell_get_shell_surface(shell, surface))) {
|
||||
wl_shell_surface_add_listener(window->shell_surface, &shell_surface_listener, window);
|
||||
wl_shell_surface_set_title(window->shell_surface, "bemenu");
|
||||
wl_shell_surface_set_class(window->shell_surface, "bemenu");
|
||||
wl_shell_surface_set_toplevel(window->shell_surface);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
window->width = 800;
|
||||
window->height = 240;
|
||||
|
||||
window->shm = shm;
|
||||
window->surface = surface;
|
||||
wl_surface_damage(surface, 0, 0, window->width, window->height);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* vim: set ts=8 sw=4 tw=0 :*/
|
|
@ -0,0 +1,422 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="xdg_shell">
|
||||
|
||||
<copyright>
|
||||
Copyright © 2008-2013 Kristian Høgsberg
|
||||
Copyright © 2013 Rafael Antognolli
|
||||
Copyright © 2013 Jasper St. Pierre
|
||||
Copyright © 2010-2013 Intel Corporation
|
||||
|
||||
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="xdg_shell" version="1">
|
||||
<description summary="create desktop-style surfaces">
|
||||
This interface is implemented by servers that provide
|
||||
desktop-style user interfaces.
|
||||
|
||||
It allows clients to associate a xdg_surface with
|
||||
a basic surface.
|
||||
</description>
|
||||
|
||||
<enum name="version">
|
||||
<description summary="latest protocol version">
|
||||
The 'current' member of this enum gives the version of the
|
||||
protocol. Implementations can compare this to the version
|
||||
they implement using static_assert to ensure the protocol and
|
||||
implementation versions match.
|
||||
</description>
|
||||
<entry name="current" value="4" summary="Always the latest version"/>
|
||||
</enum>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="role" value="0" summary="given wl_surface has another role"/>
|
||||
</enum>
|
||||
|
||||
<request name="use_unstable_version">
|
||||
<description summary="enable use of this unstable version">
|
||||
Negotiate the unstable version of the interface. This
|
||||
mechanism is in place to ensure client and server agree on the
|
||||
unstable versions of the protocol that they speak or exit
|
||||
cleanly if they don't agree. This request will go away once
|
||||
the xdg-shell protocol is stable.
|
||||
</description>
|
||||
<arg name="version" type="int"/>
|
||||
</request>
|
||||
|
||||
<request name="get_xdg_surface">
|
||||
<description summary="create a shell surface from a surface">
|
||||
Create a shell surface for an existing surface.
|
||||
|
||||
This request gives the surface the role of xdg_surface. If the
|
||||
surface already has another role, it raises a protocol error.
|
||||
|
||||
Only one shell or popup surface can be associated with a given
|
||||
surface.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="xdg_surface"/>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
</request>
|
||||
|
||||
<request name="get_xdg_popup">
|
||||
<description summary="create a shell surface from a surface">
|
||||
Create a popup surface for an existing surface.
|
||||
|
||||
This request gives the surface the role of xdg_popup. If the
|
||||
surface already has another role, it raises a protocol error.
|
||||
|
||||
Only one shell or popup surface can be associated with a given
|
||||
surface.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="xdg_popup"/>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="parent" type="object" interface="wl_surface"/>
|
||||
<arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
|
||||
<arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
|
||||
<arg name="x" type="int"/>
|
||||
<arg name="y" type="int"/>
|
||||
<arg name="flags" type="uint"/>
|
||||
</request>
|
||||
|
||||
<event name="ping">
|
||||
<description summary="check if the client is alive">
|
||||
The ping event asks the client if it's still alive. Pass the
|
||||
serial specified in the event back to the compositor by sending
|
||||
a "pong" request back with the specified serial.
|
||||
|
||||
Compositors can use this to determine if the client is still
|
||||
alive. It's unspecified what will happen if the client doesn't
|
||||
respond to the ping request, or in what timeframe. Clients should
|
||||
try to respond in a reasonable amount of time.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="pass this to the callback"/>
|
||||
</event>
|
||||
|
||||
<request name="pong">
|
||||
<description summary="respond to a ping event">
|
||||
A client must respond to a ping event with a pong request or
|
||||
the client may be deemed unresponsive.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="serial of the ping event"/>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="xdg_surface" version="1">
|
||||
|
||||
<description summary="desktop-style metadata interface">
|
||||
An interface that may be implemented by a wl_surface, for
|
||||
implementations that provide a desktop-style user interface.
|
||||
|
||||
It provides requests to treat surfaces like windows, allowing to set
|
||||
properties like maximized, fullscreen, minimized, and to move and resize
|
||||
them, and associate metadata like title and app id.
|
||||
|
||||
On the server side the object is automatically destroyed when
|
||||
the related wl_surface is destroyed. On client side,
|
||||
xdg_surface.destroy() must be called before destroying
|
||||
the wl_surface object.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="remove xdg_surface interface">
|
||||
The xdg_surface interface is removed from the wl_surface object
|
||||
that was turned into a xdg_surface with
|
||||
xdg_shell.get_xdg_surface request. The xdg_surface properties,
|
||||
like maximized and fullscreen, are lost. The wl_surface loses
|
||||
its role as a xdg_surface. The wl_surface is unmapped.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="set_parent">
|
||||
<description summary="surface is a child of another surface">
|
||||
Child surfaces are stacked above their parents, and will be
|
||||
unmapped if the parent is unmapped too. They should not appear
|
||||
on task bars and alt+tab.
|
||||
</description>
|
||||
<arg name="parent" type="object" interface="wl_surface" allow-null="true"/>
|
||||
</request>
|
||||
|
||||
<request name="set_title">
|
||||
<description summary="set surface title">
|
||||
Set a short title for the surface.
|
||||
|
||||
This string may be used to identify the surface in a task bar,
|
||||
window list, or other user interface elements provided by the
|
||||
compositor.
|
||||
|
||||
The string must be encoded in UTF-8.
|
||||
</description>
|
||||
<arg name="title" type="string"/>
|
||||
</request>
|
||||
|
||||
<request name="set_app_id">
|
||||
<description summary="set surface class">
|
||||
Set an id for the surface.
|
||||
|
||||
The app id identifies the general class of applications to which
|
||||
the surface belongs.
|
||||
|
||||
It should be the ID that appears in the new desktop entry
|
||||
specification, the interface name.
|
||||
</description>
|
||||
<arg name="app_id" type="string"/>
|
||||
</request>
|
||||
|
||||
<request name="show_window_menu">
|
||||
<description summary="show the window menu">
|
||||
Clients implementing client-side decorations might want to show
|
||||
a context menu when right-clicking on the decorations, giving the
|
||||
user a menu that they can use to maximize or minimize the window.
|
||||
|
||||
This request asks the compositor to pop up such a window menu at
|
||||
the given position, relative to the parent surface. There are
|
||||
no guarantees as to what the window menu contains.
|
||||
|
||||
Your surface must have focus on the seat passed in to pop up the
|
||||
window menu.
|
||||
</description>
|
||||
|
||||
<arg name="seat" type="object" interface="wl_seat" summary="the seat to pop the window up on"/>
|
||||
<arg name="serial" type="uint" summary="serial of the event to pop up the window for"/>
|
||||
<arg name="x" type="int" summary="the x position to pop up the window menu at"/>
|
||||
<arg name="y" type="int" summary="the y position to pop up the window menu at"/>
|
||||
</request>
|
||||
|
||||
<request name="move">
|
||||
<description summary="start an interactive move">
|
||||
Start a pointer-driven move of the surface.
|
||||
|
||||
This request must be used in response to a button press event.
|
||||
The server may ignore move requests depending on the state of
|
||||
the surface (e.g. fullscreen or maximized).
|
||||
</description>
|
||||
<arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
|
||||
<arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
|
||||
</request>
|
||||
|
||||
<enum name="resize_edge">
|
||||
<description summary="edge values for resizing">
|
||||
These values are used to indicate which edge of a surface
|
||||
is being dragged in a resize operation. The server may
|
||||
use this information to adapt its behavior, e.g. choose
|
||||
an appropriate cursor image.
|
||||
</description>
|
||||
<entry name="none" value="0"/>
|
||||
<entry name="top" value="1"/>
|
||||
<entry name="bottom" value="2"/>
|
||||
<entry name="left" value="4"/>
|
||||
<entry name="top_left" value="5"/>
|
||||
<entry name="bottom_left" value="6"/>
|
||||
<entry name="right" value="8"/>
|
||||
<entry name="top_right" value="9"/>
|
||||
<entry name="bottom_right" value="10"/>
|
||||
</enum>
|
||||
|
||||
<request name="resize">
|
||||
<description summary="start an interactive resize">
|
||||
Start a pointer-driven resizing of the surface.
|
||||
|
||||
This request must be used in response to a button press event.
|
||||
The server may ignore resize requests depending on the state of
|
||||
the surface (e.g. fullscreen or maximized).
|
||||
</description>
|
||||
<arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
|
||||
<arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
|
||||
<arg name="edges" type="uint" summary="which edge or corner is being dragged"/>
|
||||
</request>
|
||||
|
||||
<enum name="state">
|
||||
<description summary="types of state on the surface">
|
||||
The different state values used on the surface. This is designed for
|
||||
state values like maximized, fullscreen. It is paired with the
|
||||
configure event to ensure that both the client and the compositor
|
||||
setting the state can be synchronized.
|
||||
|
||||
States set in this way are double-buffered. They will get applied on
|
||||
the next commit.
|
||||
|
||||
Desktop environments may extend this enum by taking up a range of
|
||||
values and documenting the range they chose in this description.
|
||||
They are not required to document the values for the range that they
|
||||
chose. Ideally, any good extensions from a desktop environment should
|
||||
make its way into standardization into this enum.
|
||||
|
||||
The current reserved ranges are:
|
||||
|
||||
0x0000 - 0x0FFF: xdg-shell core values, documented below.
|
||||
0x1000 - 0x1FFF: GNOME
|
||||
</description>
|
||||
<entry name="maximized" value="1" summary="the surface is maximized">
|
||||
The surface is maximized. The window geometry specified in the configure
|
||||
event must be obeyed by the client.
|
||||
</entry>
|
||||
<entry name="fullscreen" value="2" summary="the surface is fullscreen">
|
||||
The surface is fullscreen. The window geometry specified in the configure
|
||||
event must be obeyed by the client.
|
||||
</entry>
|
||||
<entry name="resizing" value="3">
|
||||
The surface is being resized. The window geometry specified in the
|
||||
configure event is a maximum; the client cannot resize beyond it.
|
||||
Clients that have aspect ratio or cell sizing configuration can use
|
||||
a smaller size, however.
|
||||
</entry>
|
||||
<entry name="activated" value="4">
|
||||
Client window decorations should be painted as if the window is
|
||||
active. Do not assume this means that the window actually has
|
||||
keyboard or pointer focus.
|
||||
</entry>
|
||||
</enum>
|
||||
|
||||
<event name="configure">
|
||||
<description summary="suggest a surface change">
|
||||
The configure event asks the client to resize its surface.
|
||||
|
||||
The width and height arguments specify a hint to the window
|
||||
about how its surface should be resized in window geometry
|
||||
coordinates. The states listed in the event specify how the
|
||||
width/height arguments should be interpreted.
|
||||
|
||||
A client should arrange a new surface, and then send a
|
||||
ack_configure request with the serial sent in this configure
|
||||
event before attaching a new surface.
|
||||
|
||||
If the client receives multiple configure events before it
|
||||
can respond to one, it is free to discard all but the last
|
||||
event it received.
|
||||
</description>
|
||||
|
||||
<arg name="width" type="int"/>
|
||||
<arg name="height" type="int"/>
|
||||
<arg name="states" type="array"/>
|
||||
<arg name="serial" type="uint"/>
|
||||
</event>
|
||||
|
||||
<request name="ack_configure">
|
||||
<description summary="ack a configure event">
|
||||
When a configure event is received, a client should then ack it
|
||||
using the ack_configure request to ensure that the compositor
|
||||
knows the client has seen the event.
|
||||
|
||||
By this point, the state is confirmed, and the next attach should
|
||||
contain the buffer drawn for the configure event you are acking.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="a serial to configure for"/>
|
||||
</request>
|
||||
|
||||
<request name="set_window_geometry">
|
||||
<description summary="set the new window geometry">
|
||||
The window geometry of a window is its "visible bounds" from the
|
||||
user's perspective. Client-side decorations often have invisible
|
||||
portions like drop-shadows which should be ignored for the
|
||||
purposes of aligning, placing and constraining windows.
|
||||
|
||||
The default value is the full bounds of the surface, including any
|
||||
subsurfaces. Once the window geometry of the surface is set once,
|
||||
it is not possible to unset it, and it will remain the same until
|
||||
set_window_geometry is called again, even if a new subsurface or
|
||||
buffer is attached.
|
||||
|
||||
If responding to a configure event, the window geometry in here
|
||||
must respect the sizing negotiations specified by the states in
|
||||
the configure event.
|
||||
</description>
|
||||
<arg name="x" type="int"/>
|
||||
<arg name="y" type="int"/>
|
||||
<arg name="width" type="int"/>
|
||||
<arg name="height" type="int"/>
|
||||
</request>
|
||||
|
||||
<request name="set_maximized" />
|
||||
<request name="unset_maximized" />
|
||||
|
||||
<request name="set_fullscreen">
|
||||
<description summary="set the window as fullscreen on a monitor">
|
||||
Make the surface fullscreen.
|
||||
|
||||
You can specify an output that you would prefer to be fullscreen.
|
||||
If this value is NULL, it's up to the compositor to choose which
|
||||
display will be used to map this surface.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||
</request>
|
||||
<request name="unset_fullscreen" />
|
||||
|
||||
<request name="set_minimized" />
|
||||
|
||||
<event name="close">
|
||||
<description summary="surface wants to be closed">
|
||||
The close event is sent by the compositor when the user
|
||||
wants the surface to be closed. This should be equivalent to
|
||||
the user clicking the close button in client-side decorations,
|
||||
if your application has any...
|
||||
|
||||
This is only a request that the user intends to close your
|
||||
window. The client may choose to ignore this request, or show
|
||||
a dialog to ask the user to save their data...
|
||||
</description>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="xdg_popup" version="1">
|
||||
<description summary="desktop-style metadata interface">
|
||||
An interface that may be implemented by a wl_surface, for
|
||||
implementations that provide a desktop-style popups/menus. A popup
|
||||
surface is a transient surface with an added pointer grab.
|
||||
|
||||
An existing implicit grab will be changed to owner-events mode,
|
||||
and the popup grab will continue after the implicit grab ends
|
||||
(i.e. releasing the mouse button does not cause the popup to be
|
||||
unmapped).
|
||||
|
||||
The popup grab continues until the window is destroyed or a mouse
|
||||
button is pressed in any other clients window. A click in any of
|
||||
the clients surfaces is reported as normal, however, clicks in
|
||||
other clients surfaces will be discarded and trigger the callback.
|
||||
|
||||
The x and y arguments specify the locations of the upper left
|
||||
corner of the surface relative to the upper left corner of the
|
||||
parent surface, in surface local coordinates.
|
||||
|
||||
xdg_popup surfaces are always transient for another surface.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="remove xdg_surface interface">
|
||||
The xdg_surface interface is removed from the wl_surface object
|
||||
that was turned into a xdg_surface with
|
||||
xdg_shell.get_xdg_surface request. The xdg_surface properties,
|
||||
like maximized and fullscreen, are lost. The wl_surface loses
|
||||
its role as a xdg_surface. The wl_surface is unmapped.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="popup_done">
|
||||
<description summary="popup interaction is done">
|
||||
The popup_done event is sent out when a popup grab is broken,
|
||||
that is, when the users clicks a surface that doesn't belong
|
||||
to the client owning the popup surface.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
|
||||
</event>
|
||||
|
||||
</interface>
|
||||
</protocol>
|
80
lib/util.c
80
lib/util.c
|
@ -5,6 +5,7 @@
|
|||
#include "internal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
@ -31,6 +32,85 @@ bm_strdup(const char *string)
|
|||
return (char *)memcpy(copy, string, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Small wrapper around realloc.
|
||||
* Resizes the buffer.
|
||||
*
|
||||
* @param in_out_buffer Reference to the input buffer that will be modified on succesful resize.
|
||||
* @param in_out_size Current buffer size, will be modified with new size on succesful resize.
|
||||
* @param nsize New size to resize the buffer to.
|
||||
* @return true for succesful resize, false for failure.
|
||||
*/
|
||||
bool
|
||||
bm_resize_buffer(char **in_out_buffer, size_t *in_out_size, size_t nsize)
|
||||
{
|
||||
assert(in_out_buffer && in_out_size);
|
||||
|
||||
if (nsize == 0 || nsize <= *in_out_size)
|
||||
return false;
|
||||
|
||||
void *tmp;
|
||||
if (!(tmp = realloc(*in_out_buffer, nsize)))
|
||||
return false;
|
||||
|
||||
*in_out_buffer = tmp;
|
||||
*in_out_size = nsize;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatted printf that returns allocated char array.
|
||||
*
|
||||
* @param fmt Format as C "string".
|
||||
* @return Copy of the formatted C "string".
|
||||
*/
|
||||
char*
|
||||
bm_dprintf(const char *fmt, ...)
|
||||
{
|
||||
assert(fmt);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
size_t len = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
char *buffer;
|
||||
if (!(buffer = calloc(1, len)))
|
||||
return NULL;
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer, len, fmt, args);
|
||||
va_end(args);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatted printf that reuses and grows buffer when neccessary.
|
||||
*
|
||||
* @param in_out_buffer Reference to buffer that holds the new formatted text.
|
||||
* @param in_out_len Reference to the length of current buffer and outs as resized length.
|
||||
* @param fmt Format as C "string".
|
||||
* @param va_list Argument list.
|
||||
* @return true if successful, false if failure.
|
||||
*/
|
||||
bool
|
||||
bm_vrprintf(char **in_out_buffer, size_t *in_out_len, const char *fmt, va_list args)
|
||||
{
|
||||
assert(in_out_buffer && in_out_len && fmt);
|
||||
|
||||
va_list copy;
|
||||
va_copy(copy, args);
|
||||
|
||||
size_t len = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
|
||||
if ((!*in_out_buffer || *in_out_len < len) && !bm_resize_buffer(in_out_buffer, in_out_len, len))
|
||||
return false;
|
||||
|
||||
vsnprintf(*in_out_buffer, len, fmt, copy);
|
||||
va_end(copy);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces next token in string with '\0' and returns position for the replaced token.
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#define INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
|
||||
static const char *BM_VERSION = "@BEMENU_VERSION@";
|
||||
#define BM_VERSION "@BEMENU_VERSION@"
|
||||
|
||||
/* vim: set ts=8 sw=4 tw=0 :*/
|
||||
|
|
|
@ -22,7 +22,7 @@ main(int argc, char **argv)
|
|||
uint32_t count;
|
||||
const struct bm_renderer **renderers = bm_get_renderers(&count);
|
||||
for (int32_t i = 0; i < count; ++i) {
|
||||
struct bm_menu *menu = bm_menu_new(bm_renderer_get_name(renderers[i]));
|
||||
struct bm_menu *menu = bm_menu_new(bm_renderer_get_name(renderers[i]), BM_PRIO_ANY);
|
||||
if (!strcmp(bm_renderer_get_name(renderers[i]), "curses") && !isatty(STDIN_FILENO)) {
|
||||
assert(!menu);
|
||||
continue;
|
||||
|
|
Loading…
Reference in New Issue