Initial code

Note that numeric and shift layout don't work yet.
This commit is contained in:
Ilia Bozhinov 2019-03-02 15:14:25 +01:00
parent 44f910e549
commit 0a9b99dd76
15 changed files with 2631 additions and 0 deletions

24
meson.build Normal file
View File

@ -0,0 +1,24 @@
project(
'wf-simple-osk',
'c',
'cpp',
version: '0.1',
license: 'MIT',
meson_version: '>=0.43.0',
default_options: [
'cpp_std=c++14',
'c_std=c11',
'warning_level=2',
'werror=false',
],
)
gtkmm = dependency('gtkmm-3.0')
wayland_client = dependency('wayland-client')
wayland_protos = dependency('wayland-protocols')
add_project_link_arguments(['-rdynamic'], language:'cpp')
add_project_arguments(['-Wno-unused-parameter'], language: 'cpp')
subdir('proto')
subdir('src')

37
proto/meson.build Normal file
View File

@ -0,0 +1,37 @@
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
wayland_scanner = find_program('wayland-scanner')
wayland_scanner_code = generator(
wayland_scanner,
output: '@BASENAME@-protocol.c',
arguments: ['private-code', '@INPUT@', '@OUTPUT@'],
)
wayland_scanner_client = generator(
wayland_scanner,
output: '@BASENAME@-client-protocol.h',
arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
)
client_protocols = [
'wayfire-shell.xml',
'virtual-keyboard-unstable-v1.xml'
]
wl_protos_src = []
wl_protos_headers = []
foreach p : client_protocols
xml = join_paths(p)
wl_protos_headers += wayland_scanner_client.process(xml)
wl_protos_src += wayland_scanner_code.process(xml)
endforeach
lib_wl_protos = static_library('wl_protos', wl_protos_src + wl_protos_headers,
dependencies: [wayland_client]) # for the include directory
wf_protos = declare_dependency(
link_with: lib_wl_protos,
sources: wl_protos_headers,
)

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="virtual_keyboard_unstable_v1">
<copyright>
Copyright © 2008-2011 Kristian Høgsberg
Copyright © 2010-2013 Intel Corporation
Copyright © 2012-2013 Collabora, Ltd.
Copyright © 2018 Purism SPC
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 (including the next
paragraph) 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.
</copyright>
<interface name="zwp_virtual_keyboard_v1" version="1">
<description summary="virtual keyboard">
The virtual keyboard provides an application with requests which emulate
the behaviour of a physical keyboard.
This interface can be used by clients on its own to provide raw input
events, or it can accompany the input method protocol.
</description>
<request name="keymap">
<description summary="keyboard mapping">
Provide a file descriptor to the compositor which can be
memory-mapped to provide a keyboard mapping description.
Format carries a value from the keymap_format enumeration.
</description>
<arg name="format" type="uint" summary="keymap format"/>
<arg name="fd" type="fd" summary="keymap file descriptor"/>
<arg name="size" type="uint" summary="keymap size, in bytes"/>
</request>
<enum name="error">
<entry name="no_keymap" value="0" summary="No keymap was set"/>
</enum>
<request name="key">
<description summary="key event">
A key was pressed or released.
The time argument is a timestamp with millisecond granularity, with an
undefined base. All requests regarding a single object must share the
same clock.
Keymap must be set before issuing this request.
State carries a value from the key_state enumeration.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="key" type="uint" summary="key that produced the event"/>
<arg name="state" type="uint" summary="physical state of the key"/>
</request>
<request name="modifiers">
<description summary="modifier and group state">
Notifies the compositor that the modifier and/or group state has
changed, and it should update state.
The client should use wl_keyboard.modifiers event to synchronize its
internal state with seat state.
Keymap must be set before issuing this request.
</description>
<arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
<arg name="mods_latched" type="uint" summary="latched modifiers"/>
<arg name="mods_locked" type="uint" summary="locked modifiers"/>
<arg name="group" type="uint" summary="keyboard layout"/>
</request>
<request name="destroy" type="destructor" since="1">
<description summary="destroy the virtual keyboard keyboard object"/>
</request>
</interface>
<interface name="zwp_virtual_keyboard_manager_v1" version="1">
<description summary="virtual keyboard manager">
A virtual keyboard manager allows an application to provide keyboard
input events as if they came from a physical keyboard.
</description>
<enum name="error">
<entry name="unauthorized" value="0" summary="client not authorized to use the interface"/>
</enum>
<request name="create_virtual_keyboard">
<description summary="Create a new virtual keyboard">
Creates a new virtual keyboard associated to a seat.
If the compositor enables a keyboard to perform arbitrary actions, it
should present an error when an untrusted client requests a new
keyboard.
</description>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="id" type="new_id" interface="zwp_virtual_keyboard_v1"/>
</request>
</interface>
</protocol>

168
proto/wayfire-shell.xml Normal file
View File

@ -0,0 +1,168 @@
<protocol name="wayfire_shell">
<interface name="zwf_shell_manager_v1" version="1">
<description summary="DE integration">
The purpose of this protocol is to enable the creation of different
desktop-interface windows like panels, backgrounds, docks,
lockscreens, etc. It also aims to allow the creation of full-blown
DEs using Wayfire.
Note that in contrast to some other efforts to create a similar
protocol, such as wlr-layer-shell, this isn't a new "shell" for
giving a role to wl_surfaces. This protocol can be used with any
type of toplevel surface (xdg_toplevel, xdg_toplevel_v6, etc.)
to give them to the corresponding WM role.
</description>
<request name="get_wf_output">
<description summary="Create a wf_output from the given output"/>
<arg name="output" type="object" interface="wl_output"/>
<arg name="id" type="new_id" interface="zwf_output_v1"/>
</request>
<request name="get_wm_surface">
<description summary="Assign a role">
Assign the given role to the given surface and add it to the
given output. A client can specify a null output, in which case
the compositor will assign the surface to the focused output,
if any such output.
The role cannot be changed later, and neither can the surface be
moved to a different output, except by the compositor.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="role" type="uint" enum="zwf_wm_surface_v1.role"/>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
<arg name="id" type="new_id" interface="zwf_wm_surface_v1"/>
</request>
</interface>
<interface name="zwf_output_v1" version="1">
<description summary="A wrapper for wl_output">
Represents a single output.
Each output is managed independently from the others.
</description>
<event name="output_hide_panels">
<description summary="Autohide/Show signal">
Panels are always rendered on top, even above fullscreen windows.
If autohide is 1, the event indicates that the panels should hide
itself, by for example unmapping or sliding outside of the output.
If autohide is 0, this means that the reason for the last request
with autohide == 1 is no longer valid, i.e the panels can show
themselves.
The output_hide_panels can be called multiple times with
autohide = 1, and the panel should show itself only when
it has received a matching number of events with autohide = 0
</description>
<arg name="autohide" type="uint"/>
</event>
<request name="inhibit_output">
<description summary="Don't render the output">
Request the compositor to not render the output, so
the output usually is cleared to black color.
To enable output rendering again, call inhibit_output_done
</description>
</request>
<request name="inhibit_output_done">
<description summary="Render the output">
Stop inhibiting the output. This must be called as many times
as inhibit_output was called to actually uninhibit rendering.
The inhibit/inhibit_done requests can be called multiple times,
even from different apps, so don't assume that a call to
inhibit_done would always mean actually starting the rendering process.
</description>
</request>
</interface>
<interface name="zwf_wm_surface_v1" version="1">
<description summary="Surface with a WM role">
Represents a surface with a specific WM role.
It belongs to the output which it was created for.
</description>
<enum name="role">
<entry name="background" value="1"/>
<entry name="bottom" value="2"/>
<entry name="panel" value="3"/>
<entry name="overlay" value="4"/>
</enum>
<request name="configure">
<description summary="Move the surface to the given output-local coordinates."/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
</request>
<enum name="anchor_edge">
<entry name="top" value="1"/>
<entry name="bottom" value="2"/>
<entry name="left" value="4"/>
<entry name="right" value="8"/>
</enum>
<request name="set_anchor">
<description summary="set anchor position">
Sets the position on the screen where the compositor should
position the view. Can be reset by specifying anchor 0. If not
set, the compositor will assume manual positioning via the
configure request.
If one anchor edge is provided, the wm surface is "stuck" to
that edge.
If two anchor edges are provided, the wm surface is considered
anchored to the corner of the screen between them.
Any other anchor edge configuration is considered invalid.
</description>
<arg name="anchors" type="uint"/>
</request>
<request name="set_margin">
<description summary="set margin respective to anchored edges">
Set the offset from the anchored edges to the wm surface. This
is an alternative to the configure request. Using both will
result in undefined results.
Margin has effect only for edges the wm surface is anchored to.
</description>
<arg name="top" type="int"/>
<arg name="bottom" type="int"/>
<arg name="left" type="int"/>
<arg name="right" type="int"/>
</request>
<enum name="keyboard_focus_mode">
<entry name="no_focus" value="0"/>
<entry name="click_to_focus" value="1"/>
<entry name="exclusive_focus" value="2"/>
</enum>
<request name="set_keyboard_mode">
<description summary="Set keyboard focus mode">
Sets how the wm surface will interact with keyboard focus.
Setting no_focus means that the surface will never receive
keyboard focus, click_to_focus means normal focus semantics (i.e
what you expect from "normal" windows), and exclusive focus means
that no other window can get keyboard focus.
</description>
<arg name="mode" type="uint" enum="keyboard_focus_mode"/>
</request>
<request name="set_exclusive_zone">
<description summary="Reserve pixels">
Request the compositor to reserve the given amount of pixels
for the wm surface(like STRUTS in X11). This has effect only
if the surface is anchored to a single edge. Margin doesn't
affect exclusive zone in any way.
</description>
<arg name="size" type="uint"/>
</request>
</interface>
</protocol>

1462
src/keymap.tpp Normal file

File diff suppressed because it is too large Load Diff

121
src/layouts.tpp Normal file
View File

@ -0,0 +1,121 @@
std::vector<std::vector<Key>> default_keys = {
{
{KEY_Q, "q", 1},
{KEY_W, "w", 1},
{KEY_E, "e", 1},
{KEY_R, "r", 1},
{KEY_T, "t", 1},
{KEY_Y, "y", 1},
{KEY_U, "u", 1},
{KEY_I, "i", 1},
{KEY_O, "o", 1},
{KEY_P, "p", 1},
{KEY_BACKSPACE, "<--", 2}
},
{
{0, " ", 0.5},
{KEY_A, "a", 1},
{KEY_S, "s", 1},
{KEY_D, "d", 1},
{KEY_F, "f", 1},
{KEY_G, "g", 1},
{KEY_H, "h", 1},
{KEY_J, "j", 1},
{KEY_K, "k", 1},
{KEY_L, "l", 1},
{KEY_ENTER, "enter", 2}
},
{
{ABC_TOGGLE, "ABC", 1},
{KEY_Z, "z", 1},
{KEY_X, "x", 1},
{KEY_C, "c", 1},
{KEY_V, "v", 1},
{KEY_B, "b", 1},
{KEY_N, "n", 1},
{KEY_M, "m", 1},
{KEY_COMMA, ",", 1},
{KEY_DOT, ".", 1}
},
{
{NUM_TOGGLE, "123?", 1.5},
{KEY_SPACE, "_", 9.5},
{KEY_LEFT, "<", 0.5},
{KEY_RIGHT, ">", 0.5},
{KEY_UP, "/\\", 0.5},
{KEY_DOWN, "\\/", 0.5}
}
};
auto shift_keys = default_keys;
for (auto& row : shift_keys)
{
for (auto& key : row)
{
for (auto& c : key.text)
{
if (std::isalpha(c))
c = std::toupper(c);
}
if (key.code < USE_SHIFT)
key.code |= USE_SHIFT;
if (key.text == "ABC")
key.text = "abc";
}
}
std::vector<std::vector<Key>> numeric_keys = {
{
{KEY_1, "1", 1},
{KEY_2, "2", 1},
{KEY_3, "3", 1},
{KEY_4, "4", 1},
{KEY_5, "5", 1},
{KEY_6, "6", 1},
{KEY_7, "7", 1},
{KEY_8, "8", 1},
{KEY_9, "9", 1},
{KEY_0, "0", 1},
{KEY_MINUS, "-", 1},
{KEY_EQUAL, "=", 1},
{KEY_BACKSPACE, "<--", 2}
},
{
{KEY_1 | USE_SHIFT, "!", 1},
{KEY_2 | USE_SHIFT, "@", 1},
{KEY_3 | USE_SHIFT, "#", 1},
{KEY_4 | USE_SHIFT, "$", 1},
{KEY_5 | USE_SHIFT, "%", 1},
{KEY_6 | USE_SHIFT, "^", 1},
{KEY_7 | USE_SHIFT, "&", 1},
{KEY_8 | USE_SHIFT, "*", 1},
{KEY_9 | USE_SHIFT, "(", 1},
{KEY_0 | USE_SHIFT, ")", 1},
{KEY_SEMICOLON, ";", 1},
{KEY_SEMICOLON | USE_SHIFT, ":", 1},
{KEY_ENTER, "ent", 1}
},
{
{KEY_LEFTBRACE, "[", 1},
{KEY_RIGHTBRACE, "]", 1},
{KEY_LEFTBRACE | USE_SHIFT, "{", 1},
{KEY_RIGHTBRACE | USE_SHIFT, "}", 1},
{KEY_COMMA | USE_SHIFT, "<", 1},
{KEY_DOT | USE_SHIFT, ">", 1},
{KEY_EQUAL | USE_SHIFT, "+", 1},
{KEY_SLASH, "/", 1},
{KEY_SLASH | USE_SHIFT, "?", 1},
{KEY_APOSTROPHE, "\'", 1},
{KEY_APOSTROPHE | USE_SHIFT, "\"", 1},
{KEY_GRAVE, "`", 1},
{KEY_GRAVE | USE_SHIFT, "~", 1}
},
{
{ABC_TOGGLE, "abc", 1},
{KEY_SPACE, "_", 10},
{KEY_BACKSLASH, "\\", 1},
{KEY_BACKSLASH | USE_SHIFT, "|", 1}
}
};

167
src/main.cpp Normal file
View File

@ -0,0 +1,167 @@
#include "osk.hpp"
#include <iostream>
#include <linux/input-event-codes.h>
#define ABC_TOGGLE 0x12345678
#define NUM_TOGGLE 0x87654321
#define IS_COMMAND(x) ((x) == ABC_TOGGLE || (x) == NUM_TOGGLE)
#define USE_SHIFT 0x10000000
namespace wf
{
namespace osk
{
int spacing = 8;
int default_width = 800;
int default_height = 400;
KeyButton::KeyButton(Key key, int width, int height)
{
this->code = key.code;
this->button.set_size_request(width, height);
this->button.set_label(key.text);
this->button.signal_pressed().connect_notify(
sigc::mem_fun(this, &KeyButton::on_pressed));
this->button.signal_released().connect_notify(
sigc::mem_fun(this, &KeyButton::on_released));
}
void KeyButton::on_pressed()
{
auto& keyboard = Keyboard::get();
if (IS_COMMAND(this->code))
return;
if (this->code & USE_SHIFT)
keyboard.get_device().set_shift(true);
keyboard.get_device().send_key(this->code & ~(USE_SHIFT),
WL_KEYBOARD_KEY_STATE_PRESSED);
}
void KeyButton::on_released()
{
auto& keyboard = Keyboard::get();
if (IS_COMMAND(this->code))
return keyboard.handle_action(this->code);
if (this->code & USE_SHIFT)
keyboard.get_device().set_shift(false);
keyboard.get_device().send_key(this->code & ~(USE_SHIFT),
WL_KEYBOARD_KEY_STATE_RELEASED);
}
KeyboardRow::KeyboardRow(std::vector<Key> keys,
int width, int height)
{
double sum = 0;
for (auto& key : keys)
sum += key.width;
box.set_spacing(spacing);
int total_spacing = std::min((int)keys.size() - 1, 0) * spacing;
int total_buttons = width - total_spacing;
for (auto& key : keys)
{
this->keys.emplace_back(std::make_unique<KeyButton>
(key, int(key.width / sum * total_buttons), height));
this->box.pack_start(this->keys.back()->button);
}
}
KeyboardLayout::KeyboardLayout(std::vector<std::vector<Key>> keys,
int32_t width, int32_t height)
{
box.set_spacing(spacing);
int total_spacing = std::min((int)keys.size() - 1, 0) * spacing;
int row_height = (height - total_spacing) / keys.size();
for (auto& row : keys)
{
this->rows.emplace_back(
std::make_unique<KeyboardRow> (row, width, row_height));
this->box.pack_start(this->rows.back()->box);
}
}
void Keyboard::init_layouts()
{
/* Key layouts are defined in layouts.tpp,
* it defines default_keys, shift_keys, numeric_keys */
#include "layouts.tpp"
this->default_layout = std::make_unique<KeyboardLayout>
(default_keys, default_width, default_height);
this->shift_layout = std::make_unique<KeyboardLayout>
(shift_keys, default_width, default_height);
this->numeric_layout = std::make_unique<KeyboardLayout>
(numeric_keys, default_width, default_height);
}
void Keyboard::set_layout(KeyboardLayout *new_layout)
{
if (this->current_layout)
this->window->remove();
this->current_layout = new_layout;
this->window->add(new_layout->box);
this->window->show_all();
}
Keyboard::Keyboard()
{
window = std::make_unique<WaylandWindow>
(default_width, default_height);
vk = std::make_unique<VirtualKeyboardDevice> ();
init_layouts();
set_layout(default_layout.get());
}
std::unique_ptr<Keyboard> Keyboard::instance;
void Keyboard::create()
{
if (instance)
throw std::logic_error("Creating keyboard twice!");
instance = std::unique_ptr<Keyboard>(new Keyboard());
}
Keyboard& Keyboard::get()
{
if (!instance)
throw std::logic_error("Getting keyboard before creating it!");
return *instance;
}
VirtualKeyboardDevice& Keyboard::get_device()
{
return *vk;
}
Gtk::Window& Keyboard::get_window()
{
return *window;
}
void Keyboard::handle_action(int32_t action)
{
}
}
}
int main()
{
auto app = Gtk::Application::create();
wf::osk::Keyboard::create();
return app->run(wf::osk::Keyboard::get().get_window());
}

3
src/meson.build Normal file
View File

@ -0,0 +1,3 @@
executable('wf-osk', ['main.cpp', 'wayland-window.cpp', 'virtual-keyboard.cpp', 'shared/os-compatibility.c'],
dependencies: [gtkmm, wf_protos],
install: true)

79
src/osk.hpp Normal file
View File

@ -0,0 +1,79 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <gtkmm.h>
#include "virtual-keyboard.hpp"
#include "wayland-window.hpp"
namespace wf
{
namespace osk
{
extern int spacing;
struct Key
{
uint32_t code;
std::string text;
double width;
};
struct KeyButton
{
Gtk::Button button;
/* keycode as in linux/input-event-codes.h */
uint32_t code;
KeyButton(Key key, int width, int height);
private:
void on_pressed();
void on_released();
};
struct KeyboardRow
{
Gtk::HBox box;
std::vector<std::unique_ptr<KeyButton>> keys;
KeyboardRow(std::vector<Key> keys,
int width, int height);
};
struct KeyboardLayout
{
Gtk::VBox box;
std::vector<std::unique_ptr<KeyboardRow>> rows;
KeyboardLayout(std::vector<std::vector<Key>> keys,
int32_t width, int32_t height);
};
class Keyboard
{
std::unique_ptr<KeyboardLayout> default_layout, shift_layout,
numeric_layout;
KeyboardLayout *current_layout = nullptr;
void init_layouts();
void set_layout(KeyboardLayout *new_layout);
std::unique_ptr<WaylandWindow> window;
std::unique_ptr<VirtualKeyboardDevice> vk;
Keyboard();
static std::unique_ptr<Keyboard> instance;
public:
static void create();
static Keyboard& get();
void handle_action(int32_t action);
VirtualKeyboardDevice& get_device();
Gtk::Window& get_window();
};
}
}

View File

@ -0,0 +1,210 @@
/*
* Copyright © 2012 Collabora, Ltd.
*
* 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 (including the
* next paragraph) 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.
*/
#define _POSIX_C_SOURCE 200809L
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/epoll.h>
#include <string.h>
#include <stdlib.h>
#include "os-compatibility.h"
int
os_fd_set_cloexec(int fd)
{
long flags;
if (fd == -1)
return -1;
flags = fcntl(fd, F_GETFD);
if (flags == -1)
return -1;
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
return -1;
return 0;
}
static int
set_cloexec_or_close(int fd)
{
if (os_fd_set_cloexec(fd) != 0) {
close(fd);
return -1;
}
return fd;
}
int
os_socketpair_cloexec(int domain, int type, int protocol, int *sv)
{
int ret;
#ifdef SOCK_CLOEXEC
ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv);
if (ret == 0 || errno != EINVAL)
return ret;
#endif
ret = socketpair(domain, type, protocol, sv);
if (ret < 0)
return ret;
sv[0] = set_cloexec_or_close(sv[0]);
sv[1] = set_cloexec_or_close(sv[1]);
if (sv[0] != -1 && sv[1] != -1)
return 0;
close(sv[0]);
close(sv[1]);
return -1;
}
int
os_epoll_create_cloexec(void)
{
int fd;
#ifdef EPOLL_CLOEXEC
fd = epoll_create1(EPOLL_CLOEXEC);
if (fd >= 0)
return fd;
if (errno != EINVAL)
return -1;
#endif
fd = epoll_create(1);
return set_cloexec_or_close(fd);
}
static int
create_tmpfile_cloexec(char *tmpname)
{
int fd;
#ifdef HAVE_MKOSTEMP
fd = mkostemp(tmpname, O_CLOEXEC);
if (fd >= 0)
unlink(tmpname);
#else
fd = mkstemp(tmpname);
if (fd >= 0) {
fd = set_cloexec_or_close(fd);
unlink(tmpname);
}
#endif
return fd;
}
/*
* Create a new, unique, anonymous file of the given size, and
* return the file descriptor for it. The file descriptor is set
* CLOEXEC. The file is immediately suitable for mmap()'ing
* the given size at offset zero.
*
* The file should not have a permanent backing store like a disk,
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
*
* The file name is deleted from the file system.
*
* The file is suitable for buffer sharing between processes by
* transmitting the file descriptor over Unix sockets using the
* SCM_RIGHTS methods.
*
* If the C library implements posix_fallocate(), it is used to
* guarantee that disk space is available for the file at the
* given size. If disk space is insufficient, errno is set to ENOSPC.
* If posix_fallocate() is not supported, program may receive
* SIGBUS on accessing mmap()'ed file contents instead.
*/
int
os_create_anonymous_file(off_t size)
{
static const char template[] = "/weston-shared-XXXXXX";
const char *path;
char *name;
int fd;
int ret;
path = getenv("XDG_RUNTIME_DIR");
if (!path) {
errno = ENOENT;
return -1;
}
name = malloc(strlen(path) + sizeof(template));
if (!name)
return -1;
strcpy(name, path);
strcat(name, template);
fd = create_tmpfile_cloexec(name);
free(name);
if (fd < 0)
return -1;
#ifdef HAVE_POSIX_FALLOCATE
do {
ret = posix_fallocate(fd, 0, size);
} while (ret == EINTR);
if (ret != 0) {
close(fd);
errno = ret;
return -1;
}
#else
do {
ret = ftruncate(fd, size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
return -1;
}
#endif
return fd;
}
#ifndef MISSING_STRCHRNUL
char *
strchrnul(const char *s, int c)
{
while (*s && *s != c)
s++;
return (char *)s;
}
#endif

View File

@ -0,0 +1,43 @@
/*
* Copyright © 2012 Collabora, Ltd.
*
* 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 (including the
* next paragraph) 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.
*/
#ifndef OS_COMPATIBILITY_H
#define OS_COMPATIBILITY_H
#include <sys/types.h>
#ifdef __cplusplus
extern "C"
{
int os_fd_set_cloexec(int fd);
int os_socketpair_cloexec(int domain, int type, int protocol, int *sv);
int os_epoll_create_cloexec(void);
int os_create_anonymous_file(off_t size);
#ifdef MISSING_STRCHRNUL
char * strchrnul(const char *s, int c);
#endif
}
#endif
#endif /* OS_COMPATIBILITY_H */

57
src/virtual-keyboard.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "virtual-keyboard.hpp"
#include "wayland-window.hpp"
#include "shared/os-compatibility.h"
#include <sys/mman.h>
#include <cstring>
#include <time.h>
#include <iostream>
namespace wf
{
VirtualKeyboardDevice::VirtualKeyboardDevice()
{
auto& display = WaylandDisplay::get();
vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
display.vk_manager, display.seat);
this->send_keymap();
}
void VirtualKeyboardDevice::send_keymap()
{
/* The keymap string is defined in keymap.tpp, it is keymap_normal */
#include "keymap.tpp"
size_t keymap_size = strlen(keymap) + 1;
int keymap_fd = os_create_anonymous_file(keymap_size);
void *ptr = mmap(NULL, keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
keymap_fd, 0);
std::strcpy((char*)ptr, keymap);
zwp_virtual_keyboard_v1_keymap(vk, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
keymap_fd, keymap_size);
}
uint32_t get_current_time()
{
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000ll + ts.tv_nsec / 1000000ll;
}
void VirtualKeyboardDevice::send_key(uint32_t key, uint32_t state) const
{
zwp_virtual_keyboard_v1_key(vk, get_current_time(), key, state);
}
void VirtualKeyboardDevice::set_shift(bool shift_on)
{
shift_pressed_counter += (shift_on ? 1 : -1);
const int modifier_shift_code = 1;
zwp_virtual_keyboard_v1_modifiers(vk,
shift_pressed_counter ? modifier_shift_code : 0, 0, 0, 0);
}
}

21
src/virtual-keyboard.hpp Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <cstdint>
#include <virtual-keyboard-unstable-v1-client-protocol.h>
namespace wf
{
class VirtualKeyboardDevice
{
int shift_pressed_counter = 0;
void send_keymap();
zwp_virtual_keyboard_v1 *vk;
public:
VirtualKeyboardDevice();
void set_shift(bool shift_on);
void send_key(uint32_t key, uint32_t state) const;
};
}

99
src/wayland-window.cpp Normal file
View File

@ -0,0 +1,99 @@
#include "wayland-window.hpp"
#include <iostream>
#include <gdk/gdkwayland.h>
#include <wayland-client.h>
namespace wf
{
// listeners
static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version)
{
auto display = static_cast<WaylandDisplay*> (data);
if (strcmp(interface, wl_seat_interface.name) == 0 && !display->seat)
{
display->seat = (wl_seat*) wl_registry_bind(registry, name,
&wl_seat_interface, std::min(version, 1u));
}
if (strcmp(interface, zwf_shell_manager_v1_interface.name) == 0)
{
display->wf_manager =
(zwf_shell_manager_v1*) wl_registry_bind(registry, name,
&zwf_shell_manager_v1_interface,
std::min(version, 1u));
}
if (strcmp(interface, zwp_virtual_keyboard_manager_v1_interface.name) == 0)
{
display->vk_manager = (zwp_virtual_keyboard_manager_v1*)
wl_registry_bind(registry, name,
&zwp_virtual_keyboard_manager_v1_interface, 1u);
}
}
static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name)
{
/* no-op */
}
static struct wl_registry_listener registry_listener =
{
&registry_add_object,
&registry_remove_object
};
WaylandDisplay::WaylandDisplay()
{
auto gdk_display = gdk_display_get_default();
auto display = gdk_wayland_display_get_wl_display(gdk_display);
if (!display)
{
std::cerr << "Failed to connect to wayland display!"
<< " Are you sure you are running a wayland compositor?" << std::endl;
std::exit(-1);
}
wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, this);
wl_display_roundtrip(display);
if (!vk_manager || !seat || !wf_manager)
{
std::cerr << "Compositor doesn't support the virtual-keyboard-v1 "
<< "and/or the wayfire-shell protocols, exiting" << std::endl;
std::exit(-1);
}
}
WaylandDisplay& WaylandDisplay::get()
{
static WaylandDisplay instance;
return instance;
}
WaylandWindow::WaylandWindow(int width, int height)
: Gtk::Window()
{
auto display = WaylandDisplay::get();
this->set_size_request(width, height);
this->show_all();
auto gdk_window = this->get_window()->gobj();
auto surface = gdk_wayland_window_get_wl_surface(gdk_window);
if (!surface)
{
std::cerr << "Error: created window was not a wayland surface" << std::endl;
std::exit(-1);
}
wm_surface = zwf_shell_manager_v1_get_wm_surface(display.wf_manager,
surface, ZWF_WM_SURFACE_V1_ROLE_OVERLAY, NULL);
zwf_wm_surface_v1_set_keyboard_mode(wm_surface,
ZWF_WM_SURFACE_V1_KEYBOARD_FOCUS_MODE_NO_FOCUS);
}
}

27
src/wayland-window.hpp Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <gtkmm/window.h>
#include <wayfire-shell-client-protocol.h>
#include <virtual-keyboard-unstable-v1-client-protocol.h>
namespace wf
{
class WaylandDisplay
{
WaylandDisplay();
public:
static WaylandDisplay& get();
wl_seat *seat = nullptr;
zwf_shell_manager_v1 *wf_manager = nullptr;
zwp_virtual_keyboard_manager_v1 *vk_manager = nullptr;
};
class WaylandWindow : public Gtk::Window
{
zwf_wm_surface_v1 *wm_surface;
public:
WaylandWindow(int width, int height);
};
}