diff --git a/meson.build b/meson.build index b9e368d..4f3e6ca 100644 --- a/meson.build +++ b/meson.build @@ -1,30 +1,30 @@ project( 'wf-simple-osk', - 'c', + 'c', 'cpp', version: '0.1', license: 'MIT', meson_version: '>=0.43.0', default_options: [ 'cpp_std=c++17', - 'c_std=c11', + 'c_std=c11', 'warning_level=2', 'werror=false', ], ) -gtkmm = dependency('gtkmm-3.0') +gtkmm = dependency('gtkmm-4.0') wayland_client = dependency('wayland-client') wayland_protos = dependency('wayland-protocols') -gtkls = dependency('gtk-layer-shell-0') +gtkls = dependency('gtk4-layer-shell-0', version: '>=1.3.0', required: false) -add_project_link_arguments(['-rdynamic'], language:'cpp') +add_project_link_arguments(['-rdynamic'], language: 'cpp') add_project_arguments(['-Wno-unused-parameter'], language: 'cpp') subdir('proto') subdir('src') install_data( - 'wf-osk.desktop', - install_dir: '@0@/share/applications'.format(get_option('prefix')) - ) + 'wf-osk.desktop', + install_dir: '@0@/share/applications'.format(get_option('prefix')), +) \ No newline at end of file diff --git a/src/keymap.tpp b/src/keymap.tpp index d97c6ca..eb47233 100644 --- a/src/keymap.tpp +++ b/src/keymap.tpp @@ -1,4 +1,5 @@ -static const char keymap[] = "xkb_keymap {\ +static const char keymap[] = + "xkb_keymap {\ xkb_keycodes \"(unnamed)\" {\ minimum = 8;\ maximum = 255;\ @@ -1168,67 +1169,67 @@ xkb_compatibility \"(unnamed)\" {\ xkb_symbols \"(unnamed)\" {\ name[group1]=\"English (US)\";\ \ - key { [ Escape ] };\ - key { [ 1, exclam ] };\ - key { [ 2, at ] };\ - key { [ 3, numbersign ] };\ - key { [ 4, dollar ] };\ - key { [ 5, percent ] };\ - key { [ 6, asciicircum ] };\ - key { [ 7, ampersand ] };\ - key { [ 8, asterisk ] };\ - key { [ 9, parenleft ] };\ - key { [ 0, parenright ] };\ - key { [ minus, underscore ] };\ - key { [ equal, plus ] };\ - key { [ BackSpace, BackSpace ] };\ - key { [ Tab, ISO_Left_Tab ] };\ - key { [ q, Q, 1 ] };\ - key { [ w, W, 2 ] };\ - key { [ e, E, 3 ] };\ - key { [ r, R, 4 ] };\ - key { [ t, T, 5 ] };\ - key { [ y, Y, 6 ] };\ - key { [ u, U, 7 ] };\ - key { [ i, I, 8 ] };\ - key { [ o, O, 9 ] };\ - key { [ p, P, 0 ] };\ - key { [ bracketleft, braceleft ] };\ - key { [ bracketright, braceright ] };\ - key { [ Return ] };\ - key { [ Control_L ] };\ - key { [ a, A, minus ] };\ - key { [ s, S, at ] };\ - key { [ d, D, asterisk ] };\ - key { [ f, F, asciicircum ] };\ - key { [ g, G, colon ] };\ - key { [ h, H, semicolon ] };\ - key { [ j, J, parenleft ] };\ - key { [ k, K, parenright ] };\ - key { [ l, L, asciitilde ] };\ - key { [ semicolon, colon ] };\ - key { [ apostrophe, quotedbl ] };\ - key { [ grave, asciitilde ] };\ - key { [ Shift_L ] };\ - key { [ backslash, bar ] };\ - key { [ z, Z, slash ] };\ - key { [ x, X, apostrophe ] };\ - key { [ c, C, quotedbl ] };\ - key { [ v, V, plus ] };\ - key { [ b, B, equal ] };\ - key { [ n, N, question ] };\ - key { [ m, M, exclam ] };\ - key { [ comma, less, backslash] };\ - key { [ period, greater, bar ] };\ - key { [ slash, question ] };\ - key { [ Shift_R ] };\ + key {\t[ Escape ] };\ + key {\t[ 1, exclam ] };\ + key {\t[ 2, at ] };\ + key {\t[ 3, numbersign ] };\ + key {\t[ 4, dollar ] };\ + key {\t[ 5, percent ] };\ + key {\t[ 6, asciicircum ] };\ + key {\t[ 7, ampersand ] };\ + key {\t[ 8, asterisk ] };\ + key {\t[ 9, parenleft ] };\ + key {\t[ 0, parenright ] };\ + key {\t[ minus, underscore ] };\ + key {\t[ equal, plus ] };\ + key {\t[ BackSpace, BackSpace ] };\ + key {\t[ Tab, ISO_Left_Tab ] };\ + key {\t[ q, Q, 1 ] };\ + key {\t[ w, W, 2 ] };\ + key {\t[ e, E, 3 ] };\ + key {\t[ r, R, 4 ] };\ + key {\t[ t, T, 5 ] };\ + key {\t[ y, Y, 6 ] };\ + key {\t[ u, U, 7 ] };\ + key {\t[ i, I, 8 ] };\ + key {\t[ o, O, 9 ] };\ + key {\t[ p, P, 0 ] };\ + key {\t[ bracketleft, braceleft ] };\ + key {\t[ bracketright, braceright ] };\ + key {\t[ Return ] };\ + key {\t[ Control_L ] };\ + key {\t[ a, A, minus ] };\ + key {\t[ s, S, at ] };\ + key {\t[ d, D, asterisk ] };\ + key {\t[ f, F, asciicircum ] };\ + key {\t[ g, G, colon ] };\ + key {\t[ h, H, semicolon ] };\ + key {\t[ j, J, parenleft ] };\ + key {\t[ k, K, parenright ] };\ + key {\t[ l, L, asciitilde ] };\ + key {\t[ semicolon, colon ] };\ + key {\t[ apostrophe, quotedbl ] };\ + key {\t[ grave, asciitilde ] };\ + key {\t[ Shift_L ] };\ + key {\t[ backslash, bar ] };\ + key {\t[ z, Z, slash ] };\ + key {\t[ x, X, apostrophe ] };\ + key {\t[ c, C, quotedbl ] };\ + key {\t[ v, V, plus ] };\ + key {\t[ b, B, equal ] };\ + key {\t[ n, N, question ] };\ + key {\t[ m, M, exclam ] };\ + key {\t[ comma, less, backslash] };\ + key {\t[ period, greater, bar ] };\ + key {\t[ slash, question ] };\ + key {\t[ Shift_R ] };\ key {\ type= \"CTRL+ALT\",\ symbols[Group1]= [ KP_Multiply, KP_Multiply, KP_Multiply, KP_Multiply, XF86ClearGrab ]\ };\ - key { [ Alt_L, Meta_L ] };\ - key { [ space ] };\ - key { [ Caps_Lock ] };\ + key {\t[ Alt_L, Meta_L ] };\ + key {\t[ space ] };\ + key {\t[ Caps_Lock ] };\ key {\ type= \"CTRL+ALT\",\ symbols[Group1]= [ F1, F1, F1, F1, XF86Switch_VT_1 ]\ @@ -1269,29 +1270,29 @@ xkb_symbols \"(unnamed)\" {\ type= \"CTRL+ALT\",\ symbols[Group1]= [ F10, F10, F10, F10, XF86Switch_VT_10 ]\ };\ - key { [ Num_Lock ] };\ - key { [ Scroll_Lock ] };\ - key { [ KP_Home, KP_7 ] };\ - key { [ KP_Up, KP_8 ] };\ - key { [ KP_Prior, KP_9 ] };\ + key {\t[ Num_Lock ] };\ + key {\t[ Scroll_Lock ] };\ + key {\t[ KP_Home, KP_7 ] };\ + key {\t[ KP_Up, KP_8 ] };\ + key {\t[ KP_Prior, KP_9 ] };\ key {\ type= \"CTRL+ALT\",\ symbols[Group1]= [ KP_Subtract, KP_Subtract, KP_Subtract, KP_Subtract, XF86Prev_VMode ]\ };\ - key { [ KP_Left, KP_4 ] };\ - key { [ KP_Begin, KP_5 ] };\ - key { [ KP_Right, KP_6 ] };\ + key {\t[ KP_Left, KP_4 ] };\ + key {\t[ KP_Begin, KP_5 ] };\ + key {\t[ KP_Right, KP_6 ] };\ key {\ type= \"CTRL+ALT\",\ symbols[Group1]= [ KP_Add, KP_Add, KP_Add, KP_Add, XF86Next_VMode ]\ };\ - key { [ KP_End, KP_1 ] };\ - key { [ KP_Down, KP_2 ] };\ - key { [ KP_Next, KP_3 ] };\ - key { [ KP_Insert, KP_0 ] };\ - key { [ KP_Delete, KP_Decimal ] };\ - key { [ ISO_Level3_Shift ] };\ - key { [ less, greater, bar, brokenbar ] };\ + key {\t[ KP_End, KP_1 ] };\ + key {\t[ KP_Down, KP_2 ] };\ + key {\t[ KP_Next, KP_3 ] };\ + key {\t[ KP_Insert, KP_0 ] };\ + key {\t[ KP_Delete, KP_Decimal ] };\ + key {\t[ ISO_Level3_Shift ] };\ + key {\t[ less, greater, bar, brokenbar ] };\ key {\ type= \"CTRL+ALT\",\ symbols[Group1]= [ F11, F11, F11, F11, XF86Switch_VT_11 ]\ @@ -1300,13 +1301,13 @@ xkb_symbols \"(unnamed)\" {\ type= \"CTRL+ALT\",\ symbols[Group1]= [ F12, F12, F12, F12, XF86Switch_VT_12 ]\ };\ - key { [ Katakana ] };\ - key { [ Hiragana ] };\ - key { [ Henkan_Mode ] };\ - key { [ Hiragana_Katakana ] };\ - key { [ Muhenkan ] };\ - key { [ KP_Enter ] };\ - key { [ Control_R ] };\ + key {\t[ Katakana ] };\ + key {\t[ Hiragana ] };\ + key {\t[ Henkan_Mode ] };\ + key {\t[ Hiragana_Katakana ] };\ + key {\t[ Muhenkan ] };\ + key {\t[ KP_Enter ] };\ + key {\t[ Control_R ] };\ key {\ type= \"CTRL+ALT\",\ symbols[Group1]= [ KP_Divide, KP_Divide, KP_Divide, KP_Divide, XF86Ungrab ]\ @@ -1319,136 +1320,136 @@ xkb_symbols \"(unnamed)\" {\ type= \"TWO_LEVEL\",\ symbols[Group1]= [ Alt_R, Meta_R ]\ };\ - key { [ Linefeed ] };\ - key { [ Home ] };\ - key { [ Up ] };\ - key { [ Prior ] };\ - key { [ Left ] };\ - key { [ Right ] };\ - key { [ End ] };\ - key { [ Down ] };\ - key { [ Next ] };\ - key { [ Insert ] };\ - key { [ Delete ] };\ - key { [ XF86AudioMute ] };\ - key { [ XF86AudioLowerVolume ] };\ - key { [ XF86AudioRaiseVolume ] };\ - key { [ XF86PowerOff ] };\ - key { [ KP_Equal ] };\ - key { [ plusminus ] };\ + key {\t[ Linefeed ] };\ + key {\t[ Home ] };\ + key {\t[ Up ] };\ + key {\t[ Prior ] };\ + key {\t[ Left ] };\ + key {\t[ Right ] };\ + key {\t[ End ] };\ + key {\t[ Down ] };\ + key {\t[ Next ] };\ + key {\t[ Insert ] };\ + key {\t[ Delete ] };\ + key {\t[ XF86AudioMute ] };\ + key {\t[ XF86AudioLowerVolume ] };\ + key {\t[ XF86AudioRaiseVolume ] };\ + key {\t[ XF86PowerOff ] };\ + key {\t[ KP_Equal ] };\ + key {\t[ plusminus ] };\ key {\ type= \"PC_CONTROL_LEVEL2\",\ symbols[Group1]= [ Pause, Break ]\ };\ - key { [ XF86LaunchA ] };\ - key { [ KP_Decimal, KP_Decimal ] };\ - key { [ Hangul ] };\ - key { [ Hangul_Hanja ] };\ - key { [ Super_L ] };\ - key { [ Super_R ] };\ - key { [ Menu ] };\ - key { [ Cancel ] };\ - key { [ Redo ] };\ - key { [ SunProps ] };\ - key { [ Undo ] };\ - key { [ SunFront ] };\ - key { [ XF86Copy ] };\ - key { [ XF86Open ] };\ - key { [ XF86Paste ] };\ - key { [ Find ] };\ - key { [ XF86Cut ] };\ - key { [ Help ] };\ - key { [ XF86MenuKB ] };\ - key { [ XF86Calculator ] };\ - key { [ XF86Sleep ] };\ - key { [ XF86WakeUp ] };\ - key { [ XF86Explorer ] };\ - key { [ XF86Send ] };\ - key { [ XF86Xfer ] };\ - key { [ XF86Launch1 ] };\ - key { [ XF86Launch2 ] };\ - key { [ XF86WWW ] };\ - key { [ XF86DOS ] };\ - key { [ XF86ScreenSaver ] };\ - key { [ XF86RotateWindows ] };\ - key { [ XF86TaskPane ] };\ - key { [ XF86Mail ] };\ - key { [ XF86Favorites ] };\ - key { [ XF86MyComputer ] };\ - key { [ XF86Back ] };\ - key { [ XF86Forward ] };\ - key { [ XF86Eject ] };\ - key { [ XF86Eject, XF86Eject ] };\ - key { [ XF86AudioNext ] };\ - key { [ XF86AudioPlay, XF86AudioPause ] };\ - key { [ XF86AudioPrev ] };\ - key { [ XF86AudioStop, XF86Eject ] };\ - key { [ XF86AudioRecord ] };\ - key { [ XF86AudioRewind ] };\ - key { [ XF86Phone ] };\ - key { [ XF86Tools ] };\ - key { [ XF86HomePage ] };\ - key { [ XF86Reload ] };\ - key { [ XF86Close ] };\ - key { [ XF86ScrollUp ] };\ - key { [ XF86ScrollDown ] };\ - key { [ parenleft ] };\ - key { [ parenright ] };\ - key { [ XF86New ] };\ - key { [ Redo ] };\ - key { [ XF86Tools ] };\ - key { [ XF86Launch5 ] };\ - key { [ XF86Launch6 ] };\ - key { [ XF86Launch7 ] };\ - key { [ XF86Launch8 ] };\ - key { [ XF86Launch9 ] };\ - key { [ XF86AudioMicMute ] };\ - key { [ XF86TouchpadToggle ] };\ - key { [ XF86TouchpadOn ] };\ - key { [ XF86TouchpadOff ] };\ - key { [ Mode_switch ] };\ - key { [ NoSymbol, Alt_L ] };\ - key { [ NoSymbol, Meta_L ] };\ - key { [ NoSymbol, Super_L ] };\ - key { [ NoSymbol, Hyper_L ] };\ - key { [ XF86AudioPlay ] };\ - key { [ XF86AudioPause ] };\ - key { [ XF86Launch3 ] };\ - key { [ XF86Launch4 ] };\ - key { [ XF86LaunchB ] };\ - key { [ XF86Suspend ] };\ - key { [ XF86Close ] };\ - key { [ XF86AudioPlay ] };\ - key { [ XF86AudioForward ] };\ - key { [ Print ] };\ - key { [ XF86WebCam ] };\ - key { [ XF86AudioPreset ] };\ - key { [ XF86Mail ] };\ - key { [ XF86Messenger ] };\ - key { [ XF86Search ] };\ - key { [ XF86Go ] };\ - key { [ XF86Finance ] };\ - key { [ XF86Game ] };\ - key { [ XF86Shop ] };\ - key { [ Cancel ] };\ - key { [ XF86MonBrightnessDown ] };\ - key { [ XF86MonBrightnessUp ] };\ - key { [ XF86AudioMedia ] };\ - key { [ XF86Display ] };\ - key { [ XF86KbdLightOnOff ] };\ - key { [ XF86KbdBrightnessDown ] };\ - key { [ XF86KbdBrightnessUp ] };\ - key { [ XF86Send ] };\ - key { [ XF86Reply ] };\ - key { [ XF86MailForward ] };\ - key { [ XF86Save ] };\ - key { [ XF86Documents ] };\ - key { [ XF86Battery ] };\ - key { [ XF86Bluetooth ] };\ - key { [ XF86WLAN ] };\ - key { [ XF86UWB ] };\ - key { [ XF86WWAN ] };\ - key { [ XF86RFKill ] };\ + key {\t[ XF86LaunchA ] };\ + key {\t[ KP_Decimal, KP_Decimal ] };\ + key {\t[ Hangul ] };\ + key {\t[ Hangul_Hanja ] };\ + key {\t[ Super_L ] };\ + key {\t[ Super_R ] };\ + key {\t[ Menu ] };\ + key {\t[ Cancel ] };\ + key {\t[ Redo ] };\ + key {\t[ SunProps ] };\ + key {\t[ Undo ] };\ + key {\t[ SunFront ] };\ + key {\t[ XF86Copy ] };\ + key {\t[ XF86Open ] };\ + key {\t[ XF86Paste ] };\ + key {\t[ Find ] };\ + key {\t[ XF86Cut ] };\ + key {\t[ Help ] };\ + key {\t[ XF86MenuKB ] };\ + key {\t[ XF86Calculator ] };\ + key {\t[ XF86Sleep ] };\ + key {\t[ XF86WakeUp ] };\ + key {\t[ XF86Explorer ] };\ + key {\t[ XF86Send ] };\ + key {\t[ XF86Xfer ] };\ + key {\t[ XF86Launch1 ] };\ + key {\t[ XF86Launch2 ] };\ + key {\t[ XF86WWW ] };\ + key {\t[ XF86DOS ] };\ + key {\t[ XF86ScreenSaver ] };\ + key {\t[ XF86RotateWindows ] };\ + key {\t[ XF86TaskPane ] };\ + key {\t[ XF86Mail ] };\ + key {\t[ XF86Favorites ] };\ + key {\t[ XF86MyComputer ] };\ + key {\t[ XF86Back ] };\ + key {\t[ XF86Forward ] };\ + key {\t[ XF86Eject ] };\ + key {\t[ XF86Eject, XF86Eject ] };\ + key {\t[ XF86AudioNext ] };\ + key {\t[ XF86AudioPlay, XF86AudioPause ] };\ + key {\t[ XF86AudioPrev ] };\ + key {\t[ XF86AudioStop, XF86Eject ] };\ + key {\t[ XF86AudioRecord ] };\ + key {\t[ XF86AudioRewind ] };\ + key {\t[ XF86Phone ] };\ + key {\t[ XF86Tools ] };\ + key {\t[ XF86HomePage ] };\ + key {\t[ XF86Reload ] };\ + key {\t[ XF86Close ] };\ + key {\t[ XF86ScrollUp ] };\ + key {\t[ XF86ScrollDown ] };\ + key {\t[ parenleft ] };\ + key {\t[ parenright ] };\ + key {\t[ XF86New ] };\ + key {\t[ Redo ] };\ + key {\t[ XF86Tools ] };\ + key {\t[ XF86Launch5 ] };\ + key {\t[ XF86Launch6 ] };\ + key {\t[ XF86Launch7 ] };\ + key {\t[ XF86Launch8 ] };\ + key {\t[ XF86Launch9 ] };\ + key {\t[ XF86AudioMicMute ] };\ + key {\t[ XF86TouchpadToggle ] };\ + key {\t[ XF86TouchpadOn ] };\ + key {\t[ XF86TouchpadOff ] };\ + key {\t[ Mode_switch ] };\ + key {\t[ NoSymbol, Alt_L ] };\ + key {\t[ NoSymbol, Meta_L ] };\ + key {\t[ NoSymbol, Super_L ] };\ + key {\t[ NoSymbol, Hyper_L ] };\ + key {\t[ XF86AudioPlay ] };\ + key {\t[ XF86AudioPause ] };\ + key {\t[ XF86Launch3 ] };\ + key {\t[ XF86Launch4 ] };\ + key {\t[ XF86LaunchB ] };\ + key {\t[ XF86Suspend ] };\ + key {\t[ XF86Close ] };\ + key {\t[ XF86AudioPlay ] };\ + key {\t[ XF86AudioForward ] };\ + key {\t[ Print ] };\ + key {\t[ XF86WebCam ] };\ + key {\t[ XF86AudioPreset ] };\ + key {\t[ XF86Mail ] };\ + key {\t[ XF86Messenger ] };\ + key {\t[ XF86Search ] };\ + key {\t[ XF86Go ] };\ + key {\t[ XF86Finance ] };\ + key {\t[ XF86Game ] };\ + key {\t[ XF86Shop ] };\ + key {\t[ Cancel ] };\ + key {\t[ XF86MonBrightnessDown ] };\ + key {\t[ XF86MonBrightnessUp ] };\ + key {\t[ XF86AudioMedia ] };\ + key {\t[ XF86Display ] };\ + key {\t[ XF86KbdLightOnOff ] };\ + key {\t[ XF86KbdBrightnessDown ] };\ + key {\t[ XF86KbdBrightnessUp ] };\ + key {\t[ XF86Send ] };\ + key {\t[ XF86Reply ] };\ + key {\t[ XF86MailForward ] };\ + key {\t[ XF86Save ] };\ + key {\t[ XF86Documents ] };\ + key {\t[ XF86Battery ] };\ + key {\t[ XF86Bluetooth ] };\ + key {\t[ XF86WLAN ] };\ + key {\t[ XF86UWB ] };\ + key {\t[ XF86WWAN ] };\ + key {\t[ XF86RFKill ] };\ modifier_map Shift { , };\ modifier_map Lock { };\ modifier_map Control { , };\ diff --git a/src/layouts.tpp b/src/layouts.tpp index b2b4201..b1b774d 100644 --- a/src/layouts.tpp +++ b/src/layouts.tpp @@ -54,10 +54,14 @@ for (auto& row : shift_keys) { key.text = key.text.uppercase(); if (key.code < USE_SHIFT) + { key.code |= USE_SHIFT; + } if (key.text == "ABC") + { key.text = "abc"; + } } } diff --git a/src/main.cpp b/src/main.cpp index 24a950e..30108bf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,5 @@ +#include "gtkmm/gesture.h" +#include "gtkmm/gestureclick.h" #include "osk.hpp" #include #include @@ -14,162 +16,185 @@ namespace wf { - namespace osk +namespace osk +{ +int spacing = OSK_SPACING; +int default_width = 800; +int default_height = 400; +int headerbar_size = 60; + +std::string anchor; + +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); + + click_gesture = Gtk::GestureClick::create(); + + click_gesture->signal_pressed().connect( + sigc::mem_fun(*this, &KeyButton::on_pressed)); + click_gesture->signal_released().connect( + sigc::mem_fun(*this, &KeyButton::on_released)); + + this->button.add_controller(click_gesture); +} + +void KeyButton::on_pressed(int button, double x, double y) +{ + std::cout << "Pressed" << std::endl; + auto& keyboard = Keyboard::get(); + if (IS_COMMAND(this->code)) { - int spacing = OSK_SPACING; - int default_width = 800; - int default_height = 400; - int headerbar_size = 60; + return; + } - std::string anchor; + click_gesture->set_state(Gtk::EventSequenceState::CLAIMED); - KeyButton::KeyButton(Key key, int width, int height) - { - this->code = key.code; + if (this->code & USE_SHIFT) + { + keyboard.get_device().set_shift(true); + } - this->button.set_size_request(width, height); - this->button.set_label(key.text); + keyboard.get_device().send_key(this->code & ~(USE_SHIFT), + WL_KEYBOARD_KEY_STATE_PRESSED); +} - 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_released(int button, double x, double y) +{ + std::cout << "Released" << std::endl; + auto& keyboard = Keyboard::get(); + if (IS_COMMAND(this->code)) + { + return keyboard.handle_action(this->code); + } - void KeyButton::on_pressed() - { - auto& keyboard = Keyboard::get(); - if (IS_COMMAND(this->code)) - return; + if (this->code & USE_SHIFT) + { + keyboard.get_device().set_shift(false); + } - if (this->code & USE_SHIFT) - keyboard.get_device().set_shift(true); + keyboard.get_device().send_key(this->code & ~(USE_SHIFT), + WL_KEYBOARD_KEY_STATE_RELEASED); +} - keyboard.get_device().send_key(this->code & ~(USE_SHIFT), - WL_KEYBOARD_KEY_STATE_PRESSED); - } +KeyboardRow::KeyboardRow(std::vector keys, + int width, int height) +{ + double sum = 0; + for (auto& key : keys) + { + sum += key.width; + } - void KeyButton::on_released() - { - auto& keyboard = Keyboard::get(); - if (IS_COMMAND(this->code)) - return keyboard.handle_action(this->code); + box.set_spacing(spacing); + int total_spacing = std::min((int)keys.size() - 1, 0) * spacing; + int total_buttons = width - total_spacing; - if (this->code & USE_SHIFT) - keyboard.get_device().set_shift(false); + for (auto& key : keys) + { + this->keys.emplace_back(std::make_unique(key, int(key.width / sum * total_buttons), + height)); + this->box.append(this->keys.back()->button); + } +} - keyboard.get_device().send_key(this->code & ~(USE_SHIFT), - WL_KEYBOARD_KEY_STATE_RELEASED); - } +KeyboardLayout::KeyboardLayout(std::vector> keys, + int32_t width, int32_t height) +{ + box.set_orientation(Gtk::Orientation::VERTICAL); + box.set_spacing(spacing); + int total_spacing = std::min((int)keys.size() - 1, 0) * spacing; - KeyboardRow::KeyboardRow(std::vector 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 - (key, int(key.width / sum * total_buttons), height)); - this->box.pack_start(this->keys.back()->button); - } - } + int row_height = (height - total_spacing) / keys.size(); + for (auto& row : keys) + { + this->rows.emplace_back( + std::make_unique(row, width, row_height)); + this->box.append(this->rows.back()->box); + } +} - KeyboardLayout::KeyboardLayout(std::vector> 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 (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" - 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(default_keys, default_width, default_height); - this->default_layout = std::make_unique - (default_keys, default_width, default_height); + this->shift_layout = std::make_unique(shift_keys, default_width, default_height); - this->shift_layout = std::make_unique - (shift_keys, default_width, default_height); + this->numeric_layout = std::make_unique(numeric_keys, default_width, default_height); +} - this->numeric_layout = std::make_unique - (numeric_keys, default_width, default_height); - } +void Keyboard::set_layout(KeyboardLayout *new_layout) +{ + this->current_layout = new_layout; + window->set_widget(new_layout->box); +} - void Keyboard::set_layout(KeyboardLayout *new_layout) - { - this->current_layout = new_layout; - window->set_widget(new_layout->box); - } +Keyboard::Keyboard() +{ + window = std::make_unique(default_width, default_height, anchor, headerbar_size); + vk = std::make_unique(); - Keyboard::Keyboard() - { - window = std::make_unique - (default_width, default_height, anchor, headerbar_size); - vk = std::make_unique (); + init_layouts(); + set_layout(default_layout.get()); +} - init_layouts(); - set_layout(default_layout.get()); - } +std::unique_ptr Keyboard::instance; +void Keyboard::create() +{ + if (instance) + { + throw std::logic_error("Creating keyboard twice!"); + } - std::unique_ptr Keyboard::instance; - void Keyboard::create() - { - if (instance) - throw std::logic_error("Creating keyboard twice!"); + instance = std::unique_ptr(new Keyboard()); +} - instance = std::unique_ptr(new Keyboard()); - } +Keyboard& Keyboard::get() +{ + if (!instance) + { + throw std::logic_error("Getting keyboard before creating it!"); + } - Keyboard& Keyboard::get() - { - if (!instance) - throw std::logic_error("Getting keyboard before creating it!"); + return *instance; +} - return *instance; - } +VirtualKeyboardDevice& Keyboard::get_device() +{ + return *vk; +} - VirtualKeyboardDevice& Keyboard::get_device() - { - return *vk; - } +Gtk::Window& Keyboard::get_window() +{ + return *window; +} - Gtk::Window& Keyboard::get_window() +void Keyboard::handle_action(uint32_t action) +{ + if (action == ABC_TOGGLE) + { + if (current_layout == default_layout.get()) { - return *window; - } - - void Keyboard::handle_action(uint32_t action) + set_layout(shift_layout.get()); + } else { - if (action == ABC_TOGGLE) - { - if (current_layout == default_layout.get()) { - set_layout(shift_layout.get()); - } else { - set_layout(default_layout.get()); - } - } - - if (action == NUM_TOGGLE) - set_layout(numeric_layout.get()); + set_layout(default_layout.get()); } } + + if (action == NUM_TOGGLE) + { + set_layout(numeric_layout.get()); + } +} +} } int main(int argc, char **argv) @@ -178,26 +203,32 @@ int main(int argc, char **argv) auto cli = clara::detail::Help(show_help) | clara::detail::Opt(wf::osk::default_width, "int")["-w"]["--width"] - ("keyboard width") | + ("keyboard width") | clara::detail::Opt(wf::osk::default_height, "int")["-h"]["--height"] - ("keyboard height") | + ("keyboard height") | clara::detail::Opt(wf::osk::headerbar_size, "int")["-b"]["--headerbar-height"] - ("headerbar height") | + ("headerbar height") | clara::detail::Opt(wf::osk::anchor, "top|left|bottom|right|pinned")["-a"] - ["--anchor"]("where the keyboard should anchor in the screen"); + ["--anchor"]("where the keyboard should anchor in the screen"); auto res = cli.parse(clara::detail::Args(argc, argv)); - if (!res) { + if (!res) + { std::cerr << "Error: " << res.errorMessage() << std::endl; return 1; } - if (show_help) { + if (show_help) + { std::cout << cli << std::endl; return 0; } auto app = Gtk::Application::create(); wf::osk::Keyboard::create(); - return app->run(wf::osk::Keyboard::get().get_window()); + app->signal_activate().connect([app] () + { + app->add_window(wf::osk::Keyboard::get().get_window()); + }); + return app->run(); } diff --git a/src/osk.hpp b/src/osk.hpp index 07c38e6..cb113fe 100644 --- a/src/osk.hpp +++ b/src/osk.hpp @@ -6,74 +6,77 @@ #include +#include "gtkmm/enums.h" +#include "gtkmm/gestureclick.h" #include "virtual-keyboard.hpp" #include "wayland-window.hpp" namespace wf { - namespace osk - { - extern int spacing; - - struct Key - { - uint32_t code; - Glib::ustring 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> keys; - - KeyboardRow(std::vector keys, - int width, int height); - }; - - struct KeyboardLayout - { - Gtk::VBox box; - std::vector> rows; - - KeyboardLayout(std::vector> keys, - int32_t width, int32_t height); - }; - - class Keyboard - { - std::unique_ptr default_layout, shift_layout, - numeric_layout; - KeyboardLayout *current_layout = nullptr; - void init_layouts(); - void set_layout(KeyboardLayout *new_layout); - - std::unique_ptr window; - std::unique_ptr vk; - Keyboard(); - - static std::unique_ptr instance; - - public: - static void create(); - static Keyboard& get(); - - void handle_action(uint32_t action); - VirtualKeyboardDevice& get_device(); - Gtk::Window& get_window(); - }; - } +namespace osk +{ +extern int spacing; + +struct Key +{ + uint32_t code; + Glib::ustring 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(int, double, double); + void on_released(int, double, double); + std::shared_ptr click_gesture; +}; + +struct KeyboardRow +{ + Gtk::Box box; + std::vector> keys; + + KeyboardRow(std::vector keys, + int width, int height); +}; + +struct KeyboardLayout +{ + Gtk::Box box; + std::vector> rows; + + KeyboardLayout(std::vector> keys, + int32_t width, int32_t height); +}; + +class Keyboard +{ + std::unique_ptr default_layout, shift_layout, + numeric_layout; + KeyboardLayout *current_layout = nullptr; + void init_layouts(); + void set_layout(KeyboardLayout *new_layout); + + std::unique_ptr window; + std::unique_ptr vk; + Keyboard(); + + static std::unique_ptr instance; + + public: + static void create(); + static Keyboard& get(); + + void handle_action(uint32_t action); + VirtualKeyboardDevice& get_device(); + Gtk::Window& get_window(); +}; +} } diff --git a/src/shared/os-compatibility.c b/src/shared/os-compatibility.c index 9980d45..867c024 100644 --- a/src/shared/os-compatibility.c +++ b/src/shared/os-compatibility.c @@ -25,106 +25,96 @@ #define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include #include -#include -#include +#include #include +#include +#include +#include +#include +#include #include "os-compatibility.h" -int -os_fd_set_cloexec(int fd) -{ - long flags; +int os_fd_set_cloexec(int fd) { + long flags; - if (fd == -1) - return -1; + if (fd == -1) + return -1; - flags = fcntl(fd, F_GETFD); - if (flags == -1) - return -1; + flags = fcntl(fd, F_GETFD); + if (flags == -1) + return -1; - if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) - return -1; + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + return -1; - return 0; + return 0; } -static int -set_cloexec_or_close(int fd) -{ - if (os_fd_set_cloexec(fd) != 0) { - close(fd); - return -1; - } - return fd; +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; +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; + 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; + 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]); + 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; + if (sv[0] != -1 && sv[1] != -1) + return 0; - close(sv[0]); - close(sv[1]); - return -1; + close(sv[0]); + close(sv[1]); + return -1; } -int -os_epoll_create_cloexec(void) -{ - int fd; +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; + 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); + fd = epoll_create(1); + return set_cloexec_or_close(fd); } -static int -create_tmpfile_cloexec(char *tmpname) -{ - int fd; +static int create_tmpfile_cloexec(char *tmpname) { + int fd; #ifdef HAVE_MKOSTEMP - fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0) - unlink(tmpname); + 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); - } + fd = mkstemp(tmpname); + if (fd >= 0) { + fd = set_cloexec_or_close(fd); + unlink(tmpname); + } #endif - return fd; + return fd; } /* @@ -148,63 +138,59 @@ create_tmpfile_cloexec(char *tmpname) * 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; +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; - } + path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } - name = malloc(strlen(path) + sizeof(template)); - if (!name) - return -1; + name = malloc(strlen(path) + sizeof(template)); + if (!name) + return -1; - strcpy(name, path); - strcat(name, template); + strcpy(name, path); + strcat(name, template); - fd = create_tmpfile_cloexec(name); + fd = create_tmpfile_cloexec(name); - free(name); + free(name); - if (fd < 0) - return -1; + 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; - } + 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; - } + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + close(fd); + return -1; + } #endif - return fd; + return fd; } #ifndef MISSING_STRCHRNUL -char * -strchrnul(const char *s, int c) -{ - while (*s && *s != c) - s++; - return (char *)s; +char *strchrnul(const char *s, int c) { + while (*s && *s != c) + s++; + return (char *)s; } #endif diff --git a/src/shared/os-compatibility.h b/src/shared/os-compatibility.h index 25ba1bb..f8bcf93 100644 --- a/src/shared/os-compatibility.h +++ b/src/shared/os-compatibility.h @@ -30,13 +30,13 @@ #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 + 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 diff --git a/src/util/clara.hpp b/src/util/clara.hpp index 7e32288..dbe6bd1 100644 --- a/src/util/clara.hpp +++ b/src/util/clara.hpp @@ -11,20 +11,20 @@ #define CLARA_HPP_INCLUDED #ifndef CLARA_CONFIG_CONSOLE_WIDTH -#define CLARA_CONFIG_CONSOLE_WIDTH 80 + #define CLARA_CONFIG_CONSOLE_WIDTH 80 #endif #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH -#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH + #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH #endif #ifndef CLARA_CONFIG_OPTIONAL_TYPE -#ifdef __has_include -#if __has_include() && __cplusplus >= 201703L -#include -#define CLARA_CONFIG_OPTIONAL_TYPE std::optional -#endif -#endif + #ifdef __has_include + #if __has_include() && __cplusplus >= 201703L + #include + #define CLARA_CONFIG_OPTIONAL_TYPE std::optional + #endif + #endif #endif @@ -40,331 +40,453 @@ // This project is hosted at https://github.com/philsquared/textflowcpp #ifndef CLARA_TEXTFLOW_HPP_INCLUDED -#define CLARA_TEXTFLOW_HPP_INCLUDED - -#include -#include -#include -#include - -#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH -#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 -#endif - - -namespace clara { namespace TextFlow { - - inline auto isWhitespace( char c ) -> bool { - static std::string chars = " \t\n\r"; - return chars.find( c ) != std::string::npos; - } - inline auto isBreakableBefore( char c ) -> bool { - static std::string chars = "[({<|"; - return chars.find( c ) != std::string::npos; - } - inline auto isBreakableAfter( char c ) -> bool { - static std::string chars = "])}>.,:;*+-=&/\\"; - return chars.find( c ) != std::string::npos; - } - - class Columns; - - class Column { - std::vector m_strings; - size_t m_width = CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; - size_t m_indent = 0; - size_t m_initialIndent = std::string::npos; - - public: - class iterator { - friend Column; - - Column const& m_column; - size_t m_stringIndex = 0; - size_t m_pos = 0; + #define CLARA_TEXTFLOW_HPP_INCLUDED + + #include + #include + #include + #include + + #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH + #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 + #endif + + +namespace clara +{ +namespace TextFlow +{ +inline auto isWhitespace(char c) -> bool +{ + static std::string chars = " \t\n\r"; + return chars.find(c) != std::string::npos; +} + +inline auto isBreakableBefore(char c) -> bool +{ + static std::string chars = "[({<|"; + return chars.find(c) != std::string::npos; +} + +inline auto isBreakableAfter(char c) -> bool +{ + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find(c) != std::string::npos; +} + +class Columns; + +class Column +{ + std::vector m_strings; + size_t m_width = CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + + public: + class iterator + { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator(Column const& column, size_t stringIndex) : + m_column(column), + m_stringIndex(stringIndex) + {} - size_t m_len = 0; - size_t m_end = 0; - bool m_suffix = false; + auto line() const -> std::string const& + { + return m_column.m_strings[m_stringIndex]; + } - iterator( Column const& column, size_t stringIndex ) - : m_column( column ), - m_stringIndex( stringIndex ) - {} + auto isBoundary(size_t at) const -> bool + { + assert(at > 0); + assert(at <= line().size()); - auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + return at == line().size() || + (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || + isBreakableBefore(line()[at]) || + isBreakableAfter(line()[at - 1]); + } - auto isBoundary( size_t at ) const -> bool { - assert( at > 0 ); - assert( at <= line().size() ); + void calcLength() + { + assert(m_stringIndex < m_column.m_strings.size()); - return at == line().size() || - ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || - isBreakableBefore( line()[at] ) || - isBreakableAfter( line()[at-1] ); + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + while (m_end < line().size() && line()[m_end] != '\n') + { + ++m_end; } - void calcLength() { - assert( m_stringIndex < m_column.m_strings.size() ); - - m_suffix = false; - auto width = m_column.m_width-indent(); - m_end = m_pos; - while( m_end < line().size() && line()[m_end] != '\n' ) - ++m_end; + if (m_end < m_pos + width) + { + m_len = m_end - m_pos; + } else + { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + { + --len; + } - if( m_end < m_pos + width ) { - m_len = m_end - m_pos; + while (len > 0 && isWhitespace(line()[m_pos + len - 1])) + { + --len; } - else { - size_t len = width; - while (len > 0 && !isBoundary(m_pos + len)) - --len; - while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) - --len; - - if (len > 0) { - m_len = len; - } else { - m_suffix = true; - m_len = width - 1; - } + + if (len > 0) + { + m_len = len; + } else + { + m_suffix = true; + m_len = width - 1; } } + } - auto indent() const -> size_t { - auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; - return initial == std::string::npos ? m_column.m_indent : initial; - } + auto indent() const -> size_t + { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } - auto addIndentAndSuffix(std::string const &plain) const -> std::string { - return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); - } + auto addIndentAndSuffix(std::string const & plain) const -> std::string + { + return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); + } - public: - using difference_type = std::ptrdiff_t; - using value_type = std::string; - using pointer = value_type*; - using reference = value_type&; - using iterator_category = std::forward_iterator_tag; + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::forward_iterator_tag; - explicit iterator( Column const& column ) : m_column( column ) { - assert( m_column.m_width > m_column.m_indent ); - assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); - calcLength(); - if( m_len == 0 ) - m_stringIndex++; // Empty string + explicit iterator(Column const& column) : m_column(column) + { + assert(m_column.m_width > m_column.m_indent); + assert( + m_column.m_initialIndent == std::string::npos || + m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_len == 0) + { + m_stringIndex++; // Empty string } + } - auto operator *() const -> std::string { - assert( m_stringIndex < m_column.m_strings.size() ); - assert( m_pos <= m_end ); - return addIndentAndSuffix(line().substr(m_pos, m_len)); - } + auto operator *() const -> std::string + { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } - auto operator ++() -> iterator& { - m_pos += m_len; - if( m_pos < line().size() && line()[m_pos] == '\n' ) - m_pos += 1; - else - while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) - ++m_pos; - - if( m_pos == line().size() ) { - m_pos = 0; - ++m_stringIndex; + auto operator ++() -> iterator& + { + m_pos += m_len; + if ((m_pos < line().size()) && (line()[m_pos] == '\n')) + { + m_pos += 1; + } else + { + while (m_pos < line().size() && isWhitespace(line()[m_pos])) + { + ++m_pos; } - if( m_stringIndex < m_column.m_strings.size() ) - calcLength(); - return *this; - } - auto operator ++(int) -> iterator { - iterator prev( *this ); - operator++(); - return prev; } - auto operator ==( iterator const& other ) const -> bool { - return - m_pos == other.m_pos && - m_stringIndex == other.m_stringIndex && - &m_column == &other.m_column; - } - auto operator !=( iterator const& other ) const -> bool { - return !operator==( other ); + if (m_pos == line().size()) + { + m_pos = 0; + ++m_stringIndex; } - }; - using const_iterator = iterator; - explicit Column( std::string const& text ) { m_strings.push_back( text ); } + if (m_stringIndex < m_column.m_strings.size()) + { + calcLength(); + } - auto width( size_t newWidth ) -> Column& { - assert( newWidth > 0 ); - m_width = newWidth; return *this; } - auto indent( size_t newIndent ) -> Column& { - m_indent = newIndent; - return *this; + + auto operator ++(int) -> iterator + { + iterator prev(*this); + operator ++(); + return prev; } - auto initialIndent( size_t newIndent ) -> Column& { - m_initialIndent = newIndent; - return *this; + + auto operator ==(iterator const& other) const -> bool + { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; } - auto width() const -> size_t { return m_width; } - auto begin() const -> iterator { return iterator( *this ); } - auto end() const -> iterator { return { *this, m_strings.size() }; } + auto operator !=(iterator const& other) const -> bool + { + return !operator ==(other); + } + }; - inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { - bool first = true; - for( auto line : col ) { - if( first ) - first = false; - else - os << "\n"; - os << line; + using const_iterator = iterator; + + explicit Column(std::string const& text) + { + m_strings.push_back(text); + } + + auto width(size_t newWidth) -> Column& + { + assert(newWidth > 0); + m_width = newWidth; + return *this; + } + + auto indent(size_t newIndent) -> Column& + { + m_indent = newIndent; + return *this; + } + + auto initialIndent(size_t newIndent) -> Column& + { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t + { + return m_width; + } + + auto begin() const -> iterator + { + return iterator(*this); + } + + auto end() const -> iterator + { + return {*this, m_strings.size()}; + } + + inline friend std::ostream& operator <<(std::ostream& os, Column const& col) + { + bool first = true; + for (auto line : col) + { + if (first) + { + first = false; + } else + { + os << "\n"; } - return os; + + os << line; } - auto operator + ( Column const& other ) -> Columns; + return os; + } - auto toString() const -> std::string { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - }; + auto operator +(Column const& other) -> Columns; - class Spacer : public Column { + auto toString() const -> std::string + { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +class Spacer : public Column +{ + public: + explicit Spacer(size_t spaceWidth) : Column("") + { + width(spaceWidth); + } +}; - public: - explicit Spacer( size_t spaceWidth ) : Column( "" ) { - width( spaceWidth ); - } - }; +class Columns +{ + std::vector m_columns; - class Columns { - std::vector m_columns; + public: - public: + class iterator + { + friend Columns; + struct EndTag {}; - class iterator { - friend Columns; - struct EndTag {}; + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; - std::vector const& m_columns; - std::vector m_iterators; - size_t m_activeIterators; + iterator(Columns const& columns, EndTag) : + m_columns(columns.m_columns), + m_activeIterators(0) + { + m_iterators.reserve(m_columns.size()); - iterator( Columns const& columns, EndTag ) - : m_columns( columns.m_columns ), - m_activeIterators( 0 ) + for (auto const& col : m_columns) { - m_iterators.reserve( m_columns.size() ); - - for( auto const& col : m_columns ) - m_iterators.push_back( col.end() ); + m_iterators.push_back(col.end()); } + } - public: - using difference_type = std::ptrdiff_t; - using value_type = std::string; - using pointer = value_type*; - using reference = value_type&; - using iterator_category = std::forward_iterator_tag; + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::forward_iterator_tag; - explicit iterator( Columns const& columns ) - : m_columns( columns.m_columns ), - m_activeIterators( m_columns.size() ) - { - m_iterators.reserve( m_columns.size() ); + explicit iterator(Columns const& columns) : + m_columns(columns.m_columns), + m_activeIterators(m_columns.size()) + { + m_iterators.reserve(m_columns.size()); - for( auto const& col : m_columns ) - m_iterators.push_back( col.begin() ); + for (auto const& col : m_columns) + { + m_iterators.push_back(col.begin()); } + } - auto operator ==( iterator const& other ) const -> bool { - return m_iterators == other.m_iterators; - } - auto operator !=( iterator const& other ) const -> bool { - return m_iterators != other.m_iterators; - } - auto operator *() const -> std::string { - std::string row, padding; - - for( size_t i = 0; i < m_columns.size(); ++i ) { - auto width = m_columns[i].width(); - if( m_iterators[i] != m_columns[i].end() ) { - std::string col = *m_iterators[i]; - row += padding + col; - if( col.size() < width ) - padding = std::string( width - col.size(), ' ' ); - else - padding = ""; - } - else { - padding += std::string( width, ' ' ); + auto operator ==(iterator const& other) const -> bool + { + return m_iterators == other.m_iterators; + } + + auto operator !=(iterator const& other) const -> bool + { + return m_iterators != other.m_iterators; + } + + auto operator *() const -> std::string + { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) + { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) + { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + { + padding = std::string(width - col.size(), ' '); + } else + { + padding = ""; } + } else + { + padding += std::string(width, ' '); } - return row; } - auto operator ++() -> iterator& { - for( size_t i = 0; i < m_columns.size(); ++i ) { - if (m_iterators[i] != m_columns[i].end()) - ++m_iterators[i]; + + return row; + } + + auto operator ++() -> iterator& + { + for (size_t i = 0; i < m_columns.size(); ++i) + { + if (m_iterators[i] != m_columns[i].end()) + { + ++m_iterators[i]; } - return *this; - } - auto operator ++(int) -> iterator { - iterator prev( *this ); - operator++(); - return prev; } - }; - using const_iterator = iterator; - - auto begin() const -> iterator { return iterator( *this ); } - auto end() const -> iterator { return { *this, iterator::EndTag() }; } - auto operator += ( Column const& col ) -> Columns& { - m_columns.push_back( col ); return *this; } - auto operator + ( Column const& col ) -> Columns { - Columns combined = *this; - combined += col; - return combined; + + auto operator ++(int) -> iterator + { + iterator prev(*this); + operator ++(); + return prev; } + }; - inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { + using const_iterator = iterator; - bool first = true; - for( auto line : cols ) { - if( first ) - first = false; - else - os << "\n"; - os << line; + auto begin() const -> iterator + { + return iterator(*this); + } + + auto end() const -> iterator + { + return {*this, iterator::EndTag()}; + } + + auto operator +=(Column const& col) -> Columns& + { + m_columns.push_back(col); + return *this; + } + + auto operator +(Column const& col) -> Columns + { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator <<(std::ostream& os, Columns const& cols) + { + bool first = true; + for (auto line : cols) + { + if (first) + { + first = false; + } else + { + os << "\n"; } - return os; - } - auto toString() const -> std::string { - std::ostringstream oss; - oss << *this; - return oss.str(); + os << line; } - }; - inline auto Column::operator + ( Column const& other ) -> Columns { - Columns cols; - cols += *this; - cols += other; - return cols; + return os; + } + + auto toString() const -> std::string + { + std::ostringstream oss; + oss << *this; + return oss.str(); } -}} +}; + +inline auto Column::operator +(Column const& other) -> Columns +{ + Columns cols; + cols += *this; + cols += other; + return cols; +} +} +} #endif // CLARA_TEXTFLOW_HPP_INCLUDED @@ -376,862 +498,1215 @@ namespace clara { namespace TextFlow { #include #include -#if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) -#define CLARA_PLATFORM_WINDOWS +#if !defined (CLARA_PLATFORM_WINDOWS) && (defined (WIN32) || defined (__WIN32__) || defined (_WIN32) || \ + defined (_MSC_VER)) + #define CLARA_PLATFORM_WINDOWS #endif -namespace clara { -namespace detail { - - // Traits for extracting arg and return type of lambdas (for single argument lambdas) - template - struct UnaryLambdaTraits : UnaryLambdaTraits {}; - - template - struct UnaryLambdaTraits { - static const bool isValid = false; - }; - - template - struct UnaryLambdaTraits { - static const bool isValid = true; - using ArgType = typename std::remove_const::type>::type; - using ReturnType = ReturnT; - }; - - class TokenStream; - - // Transport for raw args (copied from main args, or supplied via init list for testing) - class Args { - friend TokenStream; - std::string m_exeName; - std::vector m_args; - - public: - Args( int argc, char const* const* argv ) - : m_exeName(argv[0]), - m_args(argv + 1, argv + argc) {} - - Args( std::initializer_list args ) - : m_exeName( *args.begin() ), - m_args( args.begin()+1, args.end() ) - {} - - auto exeName() const -> std::string { - return m_exeName; - } - }; - - // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string - // may encode an option + its argument if the : or = form is used - enum class TokenType { - Option, Argument - }; - struct Token { - TokenType type; - std::string token; - }; - - inline auto isOptPrefix( char c ) -> bool { - return c == '-' +namespace clara +{ +namespace detail +{ +// Traits for extracting arg and return type of lambdas (for single argument lambdas) +template +struct UnaryLambdaTraits : UnaryLambdaTraits {}; + +template +struct UnaryLambdaTraits +{ + static const bool isValid = false; +}; + +template +struct UnaryLambdaTraits +{ + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type; + using ReturnType = ReturnT; +}; + +class TokenStream; + +// Transport for raw args (copied from main args, or supplied via init list for testing) +class Args +{ + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args(int argc, char const*const *argv) : + m_exeName(argv[0]), + m_args(argv + 1, argv + argc) + {} + + Args(std::initializer_list args) : + m_exeName(*args.begin()), + m_args(args.begin() + 1, args.end()) + {} + + auto exeName() const -> std::string + { + return m_exeName; + } +}; + +// Wraps a token coming from a token stream. These may not directly correspond to strings as a single string +// may encode an option + its argument if the : or = form is used +enum class TokenType +{ + Option, + Argument, +}; + +struct Token +{ + TokenType type; + std::string token; +}; + +inline auto isOptPrefix(char c) -> bool +{ + return c == '-' #ifdef CLARA_PLATFORM_WINDOWS - || c == '/' + || c == '/' #endif - ; - } - - // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled - class TokenStream { - using Iterator = std::vector::const_iterator; - Iterator it; - Iterator itEnd; - std::vector m_tokenBuffer; - - void loadBuffer() { - m_tokenBuffer.resize( 0 ); - - // Skip any empty strings - while( it != itEnd && it->empty() ) - ++it; + ; +} + +// Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled +class TokenStream +{ + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() + { + m_tokenBuffer.resize(0); + + // Skip any empty strings + while (it != itEnd && it->empty()) + { + ++it; + } - if( it != itEnd ) { - auto const &next = *it; - if( isOptPrefix( next[0] ) ) { - auto delimiterPos = next.find_first_of( " :=" ); - if( delimiterPos != std::string::npos ) { - m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); - m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); - } else { - if( next[1] != '-' && next.size() > 2 ) { - std::string opt = "- "; - for( size_t i = 1; i < next.size(); ++i ) { - opt[1] = next[i]; - m_tokenBuffer.push_back( { TokenType::Option, opt } ); - } - } else { - m_tokenBuffer.push_back( { TokenType::Option, next } ); + if (it != itEnd) + { + auto const & next = *it; + if (isOptPrefix(next[0])) + { + auto delimiterPos = next.find_first_of(" :="); + if (delimiterPos != std::string::npos) + { + m_tokenBuffer.push_back({TokenType::Option, next.substr(0, delimiterPos)}); + m_tokenBuffer.push_back({TokenType::Argument, next.substr(delimiterPos + 1)}); + } else + { + if ((next[1] != '-') && (next.size() > 2)) + { + std::string opt = "- "; + for (size_t i = 1; i < next.size(); ++i) + { + opt[1] = next[i]; + m_tokenBuffer.push_back({TokenType::Option, opt}); } + } else + { + m_tokenBuffer.push_back({TokenType::Option, next}); } - } else { - m_tokenBuffer.push_back( { TokenType::Argument, next } ); } + } else + { + m_tokenBuffer.push_back({TokenType::Argument, next}); } } + } - public: - explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + public: + explicit TokenStream(Args const & args) : TokenStream(args.m_args.begin(), args.m_args.end()) + {} - TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { - loadBuffer(); - } + TokenStream(Iterator it, Iterator itEnd) : it(it), itEnd(itEnd) + { + loadBuffer(); + } - explicit operator bool() const { - return !m_tokenBuffer.empty() || it != itEnd; - } + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } - auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + auto count() const -> size_t + { + return m_tokenBuffer.size() + (itEnd - it); + } - auto operator*() const -> Token { - assert( !m_tokenBuffer.empty() ); - return m_tokenBuffer.front(); - } + auto operator *() const -> Token + { + assert(!m_tokenBuffer.empty()); + return m_tokenBuffer.front(); + } - auto operator->() const -> Token const * { - assert( !m_tokenBuffer.empty() ); - return &m_tokenBuffer.front(); - } + auto operator ->() const -> Token const* + { + assert(!m_tokenBuffer.empty()); + return &m_tokenBuffer.front(); + } - auto operator++() -> TokenStream & { - if( m_tokenBuffer.size() >= 2 ) { - m_tokenBuffer.erase( m_tokenBuffer.begin() ); - } else { - if( it != itEnd ) - ++it; - loadBuffer(); + auto operator ++() -> TokenStream & + { + if (m_tokenBuffer.size() >= 2) + { + m_tokenBuffer.erase(m_tokenBuffer.begin()); + } else + { + if (it != itEnd) + { + ++it; } - return *this; - } - }; + loadBuffer(); + } - class ResultBase { - public: - enum Type { - Ok, LogicError, RuntimeError - }; - - protected: - ResultBase( Type type ) : m_type( type ) {} - virtual ~ResultBase() = default; - - virtual void enforceOk() const = 0; + return *this; + } +}; + + +class ResultBase +{ + public: + enum Type + { + Ok, + LogicError, + RuntimeError, +}; + +protected: +ResultBase(Type type) : m_type(type) +{} +virtual ~ResultBase() = default; + +virtual void enforceOk() const = 0; + +Type m_type; +}; + +template +class ResultValueBase : public ResultBase +{ + public: + auto value() const -> T const & + { + enforceOk(); + return m_value; + } - Type m_type; - }; + protected: + ResultValueBase(Type type) : ResultBase(type) + {} - template - class ResultValueBase : public ResultBase { - public: - auto value() const -> T const & { - enforceOk(); - return m_value; + ResultValueBase(ResultValueBase const & other) : ResultBase(other) + { + if (m_type == ResultBase::Ok) + { + new(&m_value) T(other.m_value); } + } - protected: - ResultValueBase( Type type ) : ResultBase( type ) {} + ResultValueBase(Type, T const & value) : ResultBase(Ok) + { + new(&m_value) T(value); + } - ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { - if( m_type == ResultBase::Ok ) - new( &m_value ) T( other.m_value ); + auto operator =(ResultValueBase const & other) -> ResultValueBase & + { + if (m_type == ResultBase::Ok) + { + m_value.~T(); } - ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { - new( &m_value ) T( value ); + ResultBase::operator =(other); + if (m_type == ResultBase::Ok) + { + new(&m_value) T(other.m_value); } - auto operator=( ResultValueBase const &other ) -> ResultValueBase & { - if( m_type == ResultBase::Ok ) - m_value.~T(); - ResultBase::operator=(other); - if( m_type == ResultBase::Ok ) - new( &m_value ) T( other.m_value ); - return *this; - } + return *this; + } - ~ResultValueBase() override { - if( m_type == Ok ) - m_value.~T(); + ~ResultValueBase() override + { + if (m_type == Ok) + { + m_value.~T(); } + } - union { - T m_value; - }; + union + { + T m_value; }; +}; + +template<> +class ResultValueBase : public ResultBase +{ + protected: + using ResultBase::ResultBase; +}; + +template +class BasicResult : public ResultValueBase +{ + public: + template + explicit BasicResult(BasicResult const & other) : + ResultValueBase(other.type()), + m_errorMessage(other.errorMessage()) + { + assert(type() != ResultBase::Ok); + } - template<> - class ResultValueBase : public ResultBase { - protected: - using ResultBase::ResultBase; - }; + template + static auto ok(U const & value) -> BasicResult + { + return {ResultBase::Ok, value}; + } - template - class BasicResult : public ResultValueBase { - public: - template - explicit BasicResult( BasicResult const &other ) - : ResultValueBase( other.type() ), - m_errorMessage( other.errorMessage() ) - { - assert( type() != ResultBase::Ok ); - } + static auto ok() -> BasicResult + { + return {ResultBase::Ok}; + } - template - static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } - static auto ok() -> BasicResult { return { ResultBase::Ok }; } - static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } - static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } - - explicit operator bool() const { return m_type == ResultBase::Ok; } - auto type() const -> ResultBase::Type { return m_type; } - auto errorMessage() const -> std::string { return m_errorMessage; } - - protected: - void enforceOk() const override { - - // Errors shouldn't reach this point, but if they do - // the actual error message will be in m_errorMessage - assert( m_type != ResultBase::LogicError ); - assert( m_type != ResultBase::RuntimeError ); - if( m_type != ResultBase::Ok ) - std::abort(); - } + static auto logicError(std::string const & message) -> BasicResult + { + return {ResultBase::LogicError, message}; + } - std::string m_errorMessage; // Only populated if resultType is an error + static auto runtimeError(std::string const & message) -> BasicResult + { + return {ResultBase::RuntimeError, message}; + } - BasicResult( ResultBase::Type type, std::string const &message ) - : ResultValueBase(type), - m_errorMessage(message) - { - assert( m_type != ResultBase::Ok ); - } + explicit operator bool() const { + return m_type == ResultBase::Ok; + } + auto type() const -> ResultBase::Type + { + return m_type; + } - using ResultValueBase::ResultValueBase; - using ResultBase::m_type; - }; + auto errorMessage() const -> std::string + { + return m_errorMessage; + } - enum class ParseResultType { - Matched, NoMatch, ShortCircuitAll, ShortCircuitSame - }; + protected: + void enforceOk() const override + { + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert(m_type != ResultBase::LogicError); + assert(m_type != ResultBase::RuntimeError); + if (m_type != ResultBase::Ok) + { + std::abort(); + } + } - class ParseState { - public: + std::string m_errorMessage; // Only populated if resultType is an error - ParseState( ParseResultType type, TokenStream const &remainingTokens ) - : m_type(type), - m_remainingTokens( remainingTokens ) - {} + BasicResult(ResultBase::Type type, std::string const & message) : + ResultValueBase(type), + m_errorMessage(message) + { + assert(m_type != ResultBase::Ok); + } - auto type() const -> ParseResultType { return m_type; } - auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; +}; + +enum class ParseResultType +{ + Matched, + NoMatch, + ShortCircuitAll, + ShortCircuitSame, +}; + +class ParseState +{ + public: + + ParseState(ParseResultType type, TokenStream const & remainingTokens) : + m_type(type), + m_remainingTokens(remainingTokens) + {} + + auto type() const -> ParseResultType + { + return m_type; + } - private: - ParseResultType m_type; - TokenStream m_remainingTokens; - }; + auto remainingTokens() const -> TokenStream + { + return m_remainingTokens; + } - using Result = BasicResult; - using ParserResult = BasicResult; - using InternalParseResult = BasicResult; + private: + ParseResultType m_type; + TokenStream m_remainingTokens; +}; + +using Result = BasicResult; +using ParserResult = BasicResult; +using InternalParseResult = BasicResult; + +struct HelpColumns +{ + std::string left; + std::string right; +}; + +template +inline auto convertInto(std::string const & source, T& target) -> ParserResult +{ + std::stringstream ss; + ss << source; + ss >> target; + if (ss.fail()) + { + return ParserResult::runtimeError("Unable to convert '" + source + "' to destination type"); + } else + { + return ParserResult::ok(ParseResultType::Matched); + } +} + +inline auto convertInto(std::string const & source, std::string& target) -> ParserResult +{ + target = source; + return ParserResult::ok(ParseResultType::Matched); +} + +inline auto convertInto(std::string const & source, bool & target) -> ParserResult +{ + std::string srcLC = source; + std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(), [] (char c) + { + return static_cast(::tolower(c)); + }); + if ((srcLC == "y") || (srcLC == "1") || (srcLC == "true") || (srcLC == "yes") || (srcLC == "on")) + { + target = true; + } else if ((srcLC == "n") || (srcLC == "0") || (srcLC == "false") || (srcLC == "no") || (srcLC == "off")) + { + target = false; + } else + { + return ParserResult::runtimeError("Expected a boolean value but did not recognise: '" + source + "'"); + } - struct HelpColumns { - std::string left; - std::string right; - }; + return ParserResult::ok(ParseResultType::Matched); +} - template - inline auto convertInto( std::string const &source, T& target ) -> ParserResult { - std::stringstream ss; - ss << source; - ss >> target; - if( ss.fail() ) - return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); - else - return ParserResult::ok( ParseResultType::Matched ); - } - inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { - target = source; - return ParserResult::ok( ParseResultType::Matched ); - } - inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { - std::string srcLC = source; - std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); - if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") - target = true; - else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") - target = false; - else - return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); - return ParserResult::ok( ParseResultType::Matched ); - } #ifdef CLARA_CONFIG_OPTIONAL_TYPE - template - inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { - T temp; - auto result = convertInto( source, temp ); - if( result ) - target = std::move(temp); - return result; +template +inline auto convertInto(std::string const & source, CLARA_CONFIG_OPTIONAL_TYPE& target) -> ParserResult +{ + T temp; + auto result = convertInto(source, temp); + if (result) + { + target = std::move(temp); } + + return result; +} + #endif // CLARA_CONFIG_OPTIONAL_TYPE - struct NonCopyable { - NonCopyable() = default; - NonCopyable( NonCopyable const & ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable &operator=( NonCopyable const & ) = delete; - NonCopyable &operator=( NonCopyable && ) = delete; - }; +struct NonCopyable +{ + NonCopyable() = default; + NonCopyable(NonCopyable const &) = delete; + NonCopyable(NonCopyable &&) = delete; + NonCopyable& operator =(NonCopyable const&) = delete; + NonCopyable& operator =(NonCopyable&&) = delete; +}; + +struct BoundRef : NonCopyable +{ + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool + { + return false; + } - struct BoundRef : NonCopyable { - virtual ~BoundRef() = default; - virtual auto isContainer() const -> bool { return false; } - virtual auto isFlag() const -> bool { return false; } - }; - struct BoundValueRefBase : BoundRef { - virtual auto setValue( std::string const &arg ) -> ParserResult = 0; - }; - struct BoundFlagRefBase : BoundRef { - virtual auto setFlag( bool flag ) -> ParserResult = 0; - virtual auto isFlag() const -> bool { return true; } - }; + virtual auto isFlag() const -> bool + { + return false; + } +}; + +struct BoundValueRefBase : BoundRef +{ + virtual auto setValue(std::string const & arg) -> ParserResult = 0; +}; + +struct BoundFlagRefBase : BoundRef +{ + virtual auto setFlag(bool flag) -> ParserResult = 0; + virtual auto isFlag() const -> bool + { + return true; + } +}; - template - struct BoundValueRef : BoundValueRefBase { - T &m_ref; +template +struct BoundValueRef : BoundValueRefBase +{ + T & m_ref; - explicit BoundValueRef( T &ref ) : m_ref( ref ) {} + explicit BoundValueRef(T & ref) : m_ref(ref) + {} - auto setValue( std::string const &arg ) -> ParserResult override { - return convertInto( arg, m_ref ); - } - }; + auto setValue(std::string const & arg) -> ParserResult override + { + return convertInto(arg, m_ref); + } +}; - template - struct BoundValueRef> : BoundValueRefBase { - std::vector &m_ref; +template +struct BoundValueRef> : BoundValueRefBase +{ + std::vector & m_ref; - explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} + explicit BoundValueRef(std::vector & ref) : m_ref(ref) + {} - auto isContainer() const -> bool override { return true; } + auto isContainer() const -> bool override + { + return true; + } - auto setValue( std::string const &arg ) -> ParserResult override { - T temp; - auto result = convertInto( arg, temp ); - if( result ) - m_ref.push_back( temp ); - return result; + auto setValue(std::string const & arg) -> ParserResult override + { + T temp; + auto result = convertInto(arg, temp); + if (result) + { + m_ref.push_back(temp); } - }; - struct BoundFlagRef : BoundFlagRefBase { - bool &m_ref; + return result; + } +}; - explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} +struct BoundFlagRef : BoundFlagRefBase +{ + bool & m_ref; - auto setFlag( bool flag ) -> ParserResult override { - m_ref = flag; - return ParserResult::ok( ParseResultType::Matched ); - } - }; + explicit BoundFlagRef(bool & ref) : m_ref(ref) + {} + + auto setFlag(bool flag) -> ParserResult override + { + m_ref = flag; + return ParserResult::ok(ParseResultType::Matched); + } +}; + +template +struct LambdaInvoker +{ + static_assert(std::is_same::value, + "Lambda must return void or clara::ParserResult"); + + template + static auto invoke(L const & lambda, ArgType const & arg) -> ParserResult + { + return lambda(arg); + } +}; + +template<> +struct LambdaInvoker +{ + template + static auto invoke(L const & lambda, ArgType const & arg) -> ParserResult + { + lambda(arg); + return ParserResult::ok(ParseResultType::Matched); + } +}; + +template +inline auto invokeLambda(L const & lambda, std::string const & arg) -> ParserResult +{ + ArgType temp{}; + auto result = convertInto(arg, temp); + return !result ? + result : + LambdaInvoker::ReturnType>::invoke(lambda, temp); +} + +template +struct BoundLambda : BoundValueRefBase +{ + L m_lambda; + + static_assert(UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument"); + explicit BoundLambda(L const & lambda) : m_lambda(lambda) + {} + + auto setValue(std::string const & arg) -> ParserResult override + { + return invokeLambda::ArgType>(m_lambda, arg); + } +}; - template - struct LambdaInvoker { - static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); +template +struct BoundFlagLambda : BoundFlagRefBase +{ + L m_lambda; - template - static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { - return lambda( arg ); - } - }; + static_assert(UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument"); + static_assert(std::is_same::ArgType, bool>::value, "flags must be boolean"); - template<> - struct LambdaInvoker { - template - static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { - lambda( arg ); - return ParserResult::ok( ParseResultType::Matched ); - } - }; + explicit BoundFlagLambda(L const & lambda) : m_lambda(lambda) + {} - template - inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { - ArgType temp{}; - auto result = convertInto( arg, temp ); - return !result - ? result - : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + auto setFlag(bool flag) -> ParserResult override + { + return LambdaInvoker::ReturnType>::invoke(m_lambda, flag); + } +}; + +enum class Optionality +{ + Optional, + Required, +}; + +struct Parser; + +class ParserBase +{ + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result + { + return Result::ok(); } + virtual auto parse(std::string const& exeName, + TokenStream const & tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t + { + return 1; + } - template - struct BoundLambda : BoundValueRefBase { - L m_lambda; + auto parse(Args const & args) const -> InternalParseResult + { + return parse(args.exeName(), TokenStream(args)); + } +}; - static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); - explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} +template +class ComposableParserImpl : public ParserBase +{ + public: + template + auto operator |(T const & other) const -> Parser; - auto setValue( std::string const &arg ) -> ParserResult override { - return invokeLambda::ArgType>( m_lambda, arg ); - } - }; + template + auto operator +(T const & other) const -> Parser; +}; + +// Common code and state for Args and Opts +template +class ParserRefImpl : public ComposableParserImpl +{ + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl(std::shared_ptr const & ref) : m_ref(ref) + {} + + public: + template + ParserRefImpl(T & ref, std::string const & hint) : + m_ref(std::make_shared>(ref)), + m_hint(hint) + {} + + template + ParserRefImpl(LambdaT const & ref, std::string const & hint) : + m_ref(std::make_shared>(ref)), + m_hint(hint) + {} + + auto operator ()(std::string const & description) -> DerivedT & + { + m_description = description; + return static_cast(*this); + } - template - struct BoundFlagLambda : BoundFlagRefBase { - L m_lambda; + auto optional() -> DerivedT & + { + m_optionality = Optionality::Optional; + return static_cast(*this); + } - static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); - static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + auto required() -> DerivedT & + { + m_optionality = Optionality::Required; + return static_cast(*this); + } - explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + auto isOptional() const -> bool + { + return m_optionality == Optionality::Optional; + } - auto setFlag( bool flag ) -> ParserResult override { - return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + auto cardinality() const -> size_t override + { + if (m_ref->isContainer()) + { + return 0; + } else + { + return 1; } - }; + } - enum class Optionality { Optional, Required }; + auto hint() const -> std::string + { + return m_hint; + } +}; - struct Parser; +class ExeName : public ComposableParserImpl +{ + std::shared_ptr m_name; + std::shared_ptr m_ref; - class ParserBase { - public: - virtual ~ParserBase() = default; - virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; - virtual auto cardinality() const -> size_t { return 1; } + template + static auto makeRef(LambdaT const & lambda) -> std::shared_ptr + { + return std::make_shared>(lambda); + } - auto parse( Args const &args ) const -> InternalParseResult { - return parse( args.exeName(), TokenStream( args ) ); - } - }; + public: + ExeName() : m_name(std::make_shared("")) + {} - template - class ComposableParserImpl : public ParserBase { - public: - template - auto operator|( T const &other ) const -> Parser; + explicit ExeName(std::string & ref) : ExeName() + { + m_ref = std::make_shared>(ref); + } - template - auto operator+( T const &other ) const -> Parser; - }; + template + explicit ExeName(LambdaT const& lambda) : ExeName() + { + m_ref = std::make_shared>(lambda); + } - // Common code and state for Args and Opts - template - class ParserRefImpl : public ComposableParserImpl { - protected: - Optionality m_optionality = Optionality::Optional; - std::shared_ptr m_ref; - std::string m_hint; - std::string m_description; - - explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} - - public: - template - ParserRefImpl( T &ref, std::string const &hint ) - : m_ref( std::make_shared>( ref ) ), - m_hint( hint ) - {} + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse(std::string const&, TokenStream const & tokens) const -> InternalParseResult override + { + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); + } - template - ParserRefImpl( LambdaT const &ref, std::string const &hint ) - : m_ref( std::make_shared>( ref ) ), - m_hint(hint) - {} + auto name() const -> std::string + { + return *m_name; + } - auto operator()( std::string const &description ) -> DerivedT & { - m_description = description; - return static_cast( *this ); - } + auto set(std::string const& newName) -> ParserResult + { + auto lastSlash = newName.find_last_of("\\/"); + auto filename = (lastSlash == std::string::npos) ? + newName : + newName.substr(lastSlash + 1); - auto optional() -> DerivedT & { - m_optionality = Optionality::Optional; - return static_cast( *this ); - }; + *m_name = filename; + if (m_ref) + { + return m_ref->setValue(filename); + } else + { + return ParserResult::ok(ParseResultType::Matched); + } + } +}; - auto required() -> DerivedT & { - m_optionality = Optionality::Required; - return static_cast( *this ); - }; +class Arg : public ParserRefImpl +{ + public: + using ParserRefImpl::ParserRefImpl; - auto isOptional() const -> bool { - return m_optionality == Optionality::Optional; + auto parse(std::string const &, TokenStream const & tokens) const -> InternalParseResult override + { + auto validationResult = validate(); + if (!validationResult) + { + return InternalParseResult(validationResult); } - auto cardinality() const -> size_t override { - if( m_ref->isContainer() ) - return 0; - else - return 1; + auto remainingTokens = tokens; + auto const & token = *remainingTokens; + if (token.type != TokenType::Argument) + { + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); } - auto hint() const -> std::string { return m_hint; } - }; - - class ExeName : public ComposableParserImpl { - std::shared_ptr m_name; - std::shared_ptr m_ref; + assert(!m_ref->isFlag()); + auto valueRef = static_cast(m_ref.get()); - template - static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { - return std::make_shared>( lambda) ; + auto result = valueRef->setValue(remainingTokens->token); + if (!result) + { + return InternalParseResult(result); + } else + { + return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); } + } +}; - public: - ExeName() : m_name( std::make_shared( "" ) ) {} +inline auto normaliseOpt(std::string const & optName) -> std::string +{ +#ifdef CLARA_PLATFORM_WINDOWS + if (optName[0] == '/') + { + return "-" + optName.substr(1); + } else +#endif + return optName; +} - explicit ExeName( std::string &ref ) : ExeName() { - m_ref = std::make_shared>( ref ); - } +class Opt : public ParserRefImpl +{ + protected: + std::vector m_optNames; - template - explicit ExeName( LambdaT const& lambda ) : ExeName() { - m_ref = std::make_shared>( lambda ); - } + public: + template + explicit Opt(LambdaT const & ref) : ParserRefImpl(std::make_shared>(ref)) + {} - // The exe name is not parsed out of the normal tokens, but is handled specially - auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { - return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); - } + explicit Opt(bool & ref) : ParserRefImpl(std::make_shared(ref)) + {} - auto name() const -> std::string { return *m_name; } - auto set( std::string const& newName ) -> ParserResult { + template + Opt(LambdaT const & ref, std::string const & hint) : ParserRefImpl(ref, hint) + {} - auto lastSlash = newName.find_last_of( "\\/" ); - auto filename = ( lastSlash == std::string::npos ) - ? newName - : newName.substr( lastSlash+1 ); + template + Opt(T & ref, std::string const & hint) : ParserRefImpl(ref, hint) + {} - *m_name = filename; - if( m_ref ) - return m_ref->setValue( filename ); - else - return ParserResult::ok( ParseResultType::Matched ); - } - }; + auto operator [](std::string const & optName) -> Opt & + { + m_optNames.push_back(optName); + return *this; + } - class Arg : public ParserRefImpl { - public: - using ParserRefImpl::ParserRefImpl; + auto getHelpColumns() const -> std::vector + { + std::ostringstream oss; + bool first = true; + for (auto const & opt : m_optNames) + { + if (first) + { + first = false; + } else + { + oss << ", "; + } - auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { - auto validationResult = validate(); - if( !validationResult ) - return InternalParseResult( validationResult ); + oss << opt; + } - auto remainingTokens = tokens; - auto const &token = *remainingTokens; - if( token.type != TokenType::Argument ) - return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + if (!m_hint.empty()) + { + oss << " <" << m_hint << ">"; + } - assert( !m_ref->isFlag() ); - auto valueRef = static_cast( m_ref.get() ); + return {{oss.str(), m_description}}; + } - auto result = valueRef->setValue( remainingTokens->token ); - if( !result ) - return InternalParseResult( result ); - else - return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + auto isMatch(std::string const & optToken) const -> bool + { + auto normalisedToken = normaliseOpt(optToken); + for (auto const & name : m_optNames) + { + if (normaliseOpt(name) == normalisedToken) + { + return true; + } } - }; - inline auto normaliseOpt( std::string const &optName ) -> std::string { -#ifdef CLARA_PLATFORM_WINDOWS - if( optName[0] == '/' ) - return "-" + optName.substr( 1 ); - else -#endif - return optName; + return false; } - class Opt : public ParserRefImpl { - protected: - std::vector m_optNames; + using ParserBase::parse; + + auto parse(std::string const&, TokenStream const & tokens) const -> InternalParseResult override + { + auto validationResult = validate(); + if (!validationResult) + { + return InternalParseResult(validationResult); + } - public: - template - explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + auto remainingTokens = tokens; + if (remainingTokens && (remainingTokens->type == TokenType::Option)) + { + auto const & token = *remainingTokens; + if (isMatch(token.token)) + { + if (m_ref->isFlag()) + { + auto flagRef = static_cast(m_ref.get()); + auto result = flagRef->setFlag(true); + if (!result) + { + return InternalParseResult(result); + } - explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + if (result.value() == ParseResultType::ShortCircuitAll) + { + return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); + } + } else + { + auto valueRef = static_cast(m_ref.get()); + ++remainingTokens; + if (!remainingTokens) + { + return InternalParseResult::runtimeError("Expected argument following " + + token.token); + } - template - Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + auto const & argToken = *remainingTokens; + if (argToken.type != TokenType::Argument) + { + return InternalParseResult::runtimeError("Expected argument following " + + token.token); + } - template - Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + auto result = valueRef->setValue(argToken.token); + if (!result) + { + return InternalParseResult(result); + } - auto operator[]( std::string const &optName ) -> Opt & { - m_optNames.push_back( optName ); - return *this; - } + if (result.value() == ParseResultType::ShortCircuitAll) + { + return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); + } + } - auto getHelpColumns() const -> std::vector { - std::ostringstream oss; - bool first = true; - for( auto const &opt : m_optNames ) { - if (first) - first = false; - else - oss << ", "; - oss << opt; + return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); } - if( !m_hint.empty() ) - oss << " <" << m_hint << ">"; - return { { oss.str(), m_description } }; } - auto isMatch( std::string const &optToken ) const -> bool { - auto normalisedToken = normaliseOpt( optToken ); - for( auto const &name : m_optNames ) { - if( normaliseOpt( name ) == normalisedToken ) - return true; - } - return false; + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); + } + + auto validate() const -> Result override + { + if (m_optNames.empty()) + { + return Result::logicError("No options supplied to Opt"); } - using ParserBase::parse; - - auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { - auto validationResult = validate(); - if( !validationResult ) - return InternalParseResult( validationResult ); - - auto remainingTokens = tokens; - if( remainingTokens && remainingTokens->type == TokenType::Option ) { - auto const &token = *remainingTokens; - if( isMatch(token.token ) ) { - if( m_ref->isFlag() ) { - auto flagRef = static_cast( m_ref.get() ); - auto result = flagRef->setFlag( true ); - if( !result ) - return InternalParseResult( result ); - if( result.value() == ParseResultType::ShortCircuitAll ) - return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); - } else { - auto valueRef = static_cast( m_ref.get() ); - ++remainingTokens; - if( !remainingTokens ) - return InternalParseResult::runtimeError( "Expected argument following " + token.token ); - auto const &argToken = *remainingTokens; - if( argToken.type != TokenType::Argument ) - return InternalParseResult::runtimeError( "Expected argument following " + token.token ); - auto result = valueRef->setValue( argToken.token ); - if( !result ) - return InternalParseResult( result ); - if( result.value() == ParseResultType::ShortCircuitAll ) - return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); - } - return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); - } + for (auto const & name : m_optNames) + { + if (name.empty()) + { + return Result::logicError("Option name cannot be empty"); } - return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); - } - auto validate() const -> Result override { - if( m_optNames.empty() ) - return Result::logicError( "No options supplied to Opt" ); - for( auto const &name : m_optNames ) { - if( name.empty() ) - return Result::logicError( "Option name cannot be empty" ); #ifdef CLARA_PLATFORM_WINDOWS - if( name[0] != '-' && name[0] != '/' ) - return Result::logicError( "Option name must begin with '-' or '/'" ); + if ((name[0] != '-') && (name[0] != '/')) + { + return Result::logicError("Option name must begin with '-' or '/'"); + } + #else - if( name[0] != '-' ) - return Result::logicError( "Option name must begin with '-'" ); -#endif + if (name[0] != '-') + { + return Result::logicError("Option name must begin with '-'"); } - return ParserRefImpl::validate(); + +#endif } - }; - struct Help : Opt { - Help( bool &showHelpFlag ) - : Opt([&]( bool flag ) { + return ParserRefImpl::validate(); + } +}; + +struct Help : Opt +{ + Help(bool & showHelpFlag) : + Opt([&] (bool flag) + { showHelpFlag = flag; - return ParserResult::ok( ParseResultType::ShortCircuitAll ); + return ParserResult::ok(ParseResultType::ShortCircuitAll); }) + { + static_cast(*this)("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } +}; + + +struct Parser : ParserBase +{ + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator |=(ExeName const & exeName) -> Parser & + { + m_exeName = exeName; + return *this; + } + + auto operator |=(Arg const & arg) -> Parser & + { + m_args.push_back(arg); + return *this; + } + + auto operator |=(Opt const & opt) -> Parser & + { + m_options.push_back(opt); + return *this; + } + + auto operator |=(Parser const & other) -> Parser & + { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator |(T const & other) const -> Parser + { + return Parser(*this) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template + auto operator +=(T const & other) -> Parser & + { + return operator |=(other); + } + + template + auto operator +(T const & other) const -> Parser + { + return operator |(other); + } + + auto getHelpColumns() const -> std::vector + { + std::vector cols; + for (auto const & o : m_options) { - static_cast( *this ) - ("display usage information") - ["-?"]["-h"]["--help"] - .optional(); + auto childCols = o.getHelpColumns(); + cols.insert(cols.end(), childCols.begin(), childCols.end()); } - }; + return cols; + } + + void writeToStream(std::ostream & os) const + { + if (!m_exeName.name().empty()) + { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for (auto const & arg : m_args) + { + if (first) + { + first = false; + } else + { + os << " "; + } + + if (arg.isOptional() && required) + { + os << "["; + required = false; + } - struct Parser : ParserBase { + os << "<" << arg.hint() << ">"; + if (arg.cardinality() == 0) + { + os << " ... "; + } + } - mutable ExeName m_exeName; - std::vector m_options; - std::vector m_args; + if (!required) + { + os << "]"; + } - auto operator|=( ExeName const &exeName ) -> Parser & { - m_exeName = exeName; - return *this; - } + if (!m_options.empty()) + { + os << " options"; + } - auto operator|=( Arg const &arg ) -> Parser & { - m_args.push_back(arg); - return *this; + os << "\n\nwhere options are:" << std::endl; } - auto operator|=( Opt const &opt ) -> Parser & { - m_options.push_back(opt); - return *this; + auto rows = getHelpColumns(); + size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for (auto const & cols : rows) + { + optWidth = (std::max)(optWidth, cols.left.size() + 2); } - auto operator|=( Parser const &other ) -> Parser & { - m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); - m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); - return *this; - } + optWidth = (std::min)(optWidth, consoleWidth / 2); - template - auto operator|( T const &other ) const -> Parser { - return Parser( *this ) |= other; + for (auto const & cols : rows) + { + auto row = + TextFlow::Column(cols.left).width(optWidth).indent(2) + + TextFlow::Spacer(4) + + TextFlow::Column(cols.right).width(consoleWidth - 7 - optWidth); + os << row << std::endl; } + } - // Forward deprecated interface with '+' instead of '|' - template - auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } - template - auto operator+( T const &other ) const -> Parser { return operator|( other ); } - - auto getHelpColumns() const -> std::vector { - std::vector cols; - for (auto const &o : m_options) { - auto childCols = o.getHelpColumns(); - cols.insert( cols.end(), childCols.begin(), childCols.end() ); + friend auto operator <<(std::ostream & os, Parser const & parser) -> std::ostream& + { + parser.writeToStream(os); + return os; + } + + auto validate() const -> Result override + { + for (auto const & opt : m_options) + { + auto result = opt.validate(); + if (!result) + { + return result; } - return cols; } - void writeToStream( std::ostream &os ) const { - if (!m_exeName.name().empty()) { - os << "usage:\n" << " " << m_exeName.name() << " "; - bool required = true, first = true; - for( auto const &arg : m_args ) { - if (first) - first = false; - else - os << " "; - if( arg.isOptional() && required ) { - os << "["; - required = false; - } - os << "<" << arg.hint() << ">"; - if( arg.cardinality() == 0 ) - os << " ... "; - } - if( !required ) - os << "]"; - if( !m_options.empty() ) - os << " options"; - os << "\n\nwhere options are:" << std::endl; + for (auto const & arg : m_args) + { + auto result = arg.validate(); + if (!result) + { + return result; } + } - auto rows = getHelpColumns(); - size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; - size_t optWidth = 0; - for( auto const &cols : rows ) - optWidth = (std::max)(optWidth, cols.left.size() + 2); + return Result::ok(); + } - optWidth = (std::min)(optWidth, consoleWidth/2); + using ParserBase::parse; - for( auto const &cols : rows ) { - auto row = - TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + - TextFlow::Spacer(4) + - TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); - os << row << std::endl; - } - } + auto parse(std::string const& exeName, TokenStream const & tokens) const -> InternalParseResult override + { + struct ParserInfo + { + ParserBase const *parser = nullptr; + size_t count = 0; + }; - friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { - parser.writeToStream( os ); - return os; - } + const size_t totalParsers = m_options.size() + m_args.size(); + assert(totalParsers < 512); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; - auto validate() const -> Result override { - for( auto const &opt : m_options ) { - auto result = opt.validate(); - if( !result ) - return result; + { + size_t i = 0; + for (auto const & opt : m_options) + { + parseInfos[i++].parser = &opt; } - for( auto const &arg : m_args ) { - auto result = arg.validate(); - if( !result ) - return result; + + for (auto const & arg : m_args) + { + parseInfos[i++].parser = &arg; } - return Result::ok(); } - using ParserBase::parse; - - auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + m_exeName.set(exeName); - struct ParserInfo { - ParserBase const* parser = nullptr; - size_t count = 0; - }; - const size_t totalParsers = m_options.size() + m_args.size(); - assert( totalParsers < 512 ); - // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do - ParserInfo parseInfos[512]; + auto result = InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); + while (result.value().remainingTokens()) + { + bool tokenParsed = false; + for (size_t i = 0; i < totalParsers; ++i) { - size_t i = 0; - for (auto const &opt : m_options) parseInfos[i++].parser = &opt; - for (auto const &arg : m_args) parseInfos[i++].parser = &arg; - } + auto& parseInfo = parseInfos[i]; + if ((parseInfo.parser->cardinality() == 0) || + (parseInfo.count < parseInfo.parser->cardinality())) + { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + { + return result; + } - m_exeName.set( exeName ); - - auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); - while( result.value().remainingTokens() ) { - bool tokenParsed = false; - - for( size_t i = 0; i < totalParsers; ++i ) { - auto& parseInfo = parseInfos[i]; - if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { - result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); - if (!result) - return result; - if (result.value().type() != ParseResultType::NoMatch) { - tokenParsed = true; - ++parseInfo.count; - break; - } + if (result.value().type() != ParseResultType::NoMatch) + { + tokenParsed = true; + ++parseInfo.count; + break; } } + } + + if (result.value().type() == ParseResultType::ShortCircuitAll) + { + return result; + } - if( result.value().type() == ParseResultType::ShortCircuitAll ) - return result; - if( !tokenParsed ) - return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + if (!tokenParsed) + { + return InternalParseResult::runtimeError( + "Unrecognised token: " + result.value().remainingTokens()->token); } - // !TBD Check missing required options - return result; } - }; - template - template - auto ComposableParserImpl::operator|( T const &other ) const -> Parser { - return Parser() | static_cast( *this ) | other; + // !TBD Check missing required options + return result; } -} // namespace detail +}; + +template +template +auto ComposableParserImpl::operator |(T const & other) const -> Parser +{ + return Parser() | static_cast(*this) | other; +} +}// namespace detail // A Combined parser @@ -1257,8 +1732,6 @@ using detail::ParseResultType; // Result type for parser operation using detail::ParserResult; - - -} // namespace clara +}// namespace clara #endif // CLARA_HPP_INCLUDED diff --git a/src/virtual-keyboard.cpp b/src/virtual-keyboard.cpp index d84fef5..545180a 100644 --- a/src/virtual-keyboard.cpp +++ b/src/virtual-keyboard.cpp @@ -1,4 +1,5 @@ #include "virtual-keyboard.hpp" +#include "gdk/wayland/gdkwayland.h" #include "wayland-window.hpp" #include "shared/os-compatibility.h" @@ -9,53 +10,52 @@ #include #include -#include namespace wf { - VirtualKeyboardDevice::VirtualKeyboardDevice() - { - auto& display = WaylandDisplay::get(); - auto seat = Gdk::Display::get_default()->get_default_seat(); - vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard( - display.vk_manager, gdk_wayland_seat_get_wl_seat(seat->gobj())); - - 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); - } +VirtualKeyboardDevice::VirtualKeyboardDevice() +{ + auto& display = WaylandDisplay::get(); + auto seat = Gdk::Display::get_default()->get_default_seat(); + vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard( + display.vk_manager, gdk_wayland_seat_get_wl_seat(seat->gobj())); + + 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); +} } diff --git a/src/virtual-keyboard.hpp b/src/virtual-keyboard.hpp index c67fee2..94d570b 100644 --- a/src/virtual-keyboard.hpp +++ b/src/virtual-keyboard.hpp @@ -5,17 +5,17 @@ namespace wf { - class VirtualKeyboardDevice - { - int shift_pressed_counter = 0; +class VirtualKeyboardDevice +{ + int shift_pressed_counter = 0; - void send_keymap(); - zwp_virtual_keyboard_v1 *vk; + void send_keymap(); + zwp_virtual_keyboard_v1 *vk; - public: - VirtualKeyboardDevice(); + public: + VirtualKeyboardDevice(); - void set_shift(bool shift_on); - void send_key(uint32_t key, uint32_t state) const; - }; + void set_shift(bool shift_on); + void send_key(uint32_t key, uint32_t state) const; +}; } diff --git a/src/wayland-window.cpp b/src/wayland-window.cpp index cffd159..0dac098 100644 --- a/src/wayland-window.cpp +++ b/src/wayland-window.cpp @@ -1,11 +1,11 @@ #include "wayland-window.hpp" +#include "gtkmm/enums.h" #include #include -#include -#include -#include +#include +#include #include -#include +#include #include #include @@ -13,214 +13,198 @@ 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 (data); - - if (strcmp(interface, zwf_shell_manager_v2_interface.name) == 0) - { - display->zwf_manager = - (zwf_shell_manager_v2*) wl_registry_bind(registry, name, - &zwf_shell_manager_v2_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); - } - } +// 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(data); - static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) + if (strcmp(interface, zwf_shell_manager_v2_interface.name) == 0) { - /* no-op */ + display->zwf_manager = + (zwf_shell_manager_v2*)wl_registry_bind(registry, name, + &zwf_shell_manager_v2_interface, std::min(version, 1u)); } - static struct wl_registry_listener registry_listener = + if (strcmp(interface, zwp_virtual_keyboard_manager_v1_interface.name) == 0) { - ®istry_add_object, - ®istry_remove_object - }; + 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 = +{ + ®istry_add_object, + ®istry_remove_object +}; + +WaylandDisplay::WaylandDisplay() +{ + auto gdk_display = gdk_display_get_default(); + auto display = gdk_wayland_display_get_wl_display(gdk_display); - 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, ®istry_listener, this); - wl_display_dispatch(display); - wl_display_roundtrip(display); - - if (!vk_manager) - { - std::cerr << "Compositor doesn't support the virtual-keyboard-v1 " - << "protocol, exiting" << std::endl; - std::exit(-1); - } + if (!display) + { + std::cerr << "Failed to connect to wayland display!" << + " Are you sure you are running a wayland compositor?" << std::endl; + std::exit(-1); } - WaylandDisplay& WaylandDisplay::get() + wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, this); + wl_display_dispatch(display); + wl_display_roundtrip(display); + + if (!vk_manager) { - static WaylandDisplay instance; - return instance; + std::cerr << "Compositor doesn't support the virtual-keyboard-v1 " << + "protocol, exiting" << std::endl; + std::exit(-1); } +} + +WaylandDisplay& WaylandDisplay::get() +{ + static WaylandDisplay instance; + return instance; +} - int32_t WaylandWindow::check_anchor(std::string anchor) - { - std::transform(anchor.begin(), anchor.end(), anchor.begin(), ::tolower); - - int32_t parsed_anchor = -1; - if (anchor.compare("top") == 0) - { - parsed_anchor = GTK_LAYER_SHELL_EDGE_TOP; - } else if (anchor.compare("bottom") == 0) - { - parsed_anchor = GTK_LAYER_SHELL_EDGE_BOTTOM; - } else if (anchor.compare("left") == 0) - { - parsed_anchor = GTK_LAYER_SHELL_EDGE_LEFT; - } else if (anchor.compare("right") == 0) - { - parsed_anchor = GTK_LAYER_SHELL_EDGE_RIGHT; - } else if (anchor.compare("pinned") == 0) - { - parsed_anchor = ANCHOR_PINNED_BOTTOM; - } - - return parsed_anchor; +int32_t WaylandWindow::check_anchor(std::string anchor) +{ + std::transform(anchor.begin(), anchor.end(), anchor.begin(), ::tolower); + + int32_t parsed_anchor = -1; + if (anchor.compare("top") == 0) + { + parsed_anchor = GTK_LAYER_SHELL_EDGE_TOP; + } else if (anchor.compare("bottom") == 0) + { + parsed_anchor = GTK_LAYER_SHELL_EDGE_BOTTOM; + } else if (anchor.compare("left") == 0) + { + parsed_anchor = GTK_LAYER_SHELL_EDGE_LEFT; + } else if (anchor.compare("right") == 0) + { + parsed_anchor = GTK_LAYER_SHELL_EDGE_RIGHT; + } else if (anchor.compare("pinned") == 0) + { + parsed_anchor = ANCHOR_PINNED_BOTTOM; } - void WaylandWindow::init(int width, int height, std::string anchor) - { - gtk_layer_init_for_window(this->gobj()); - gtk_layer_set_layer(this->gobj(), GTK_LAYER_SHELL_LAYER_OVERLAY); - gtk_layer_set_namespace(this->gobj(), "keyboard"); - gtk_layer_set_exclusive_zone(this->gobj(), -1); - auto layer_anchor = check_anchor(anchor); - if (layer_anchor > -1) - { - gtk_layer_set_anchor(this->gobj(), - (GtkLayerShellEdge)layer_anchor, true); - } else if (layer_anchor == ANCHOR_PINNED_BOTTOM) - { - gtk_layer_set_anchor(this->gobj(), - GTK_LAYER_SHELL_EDGE_BOTTOM, true); - gtk_layer_set_anchor(this->gobj(), - GTK_LAYER_SHELL_EDGE_LEFT, true); - gtk_layer_set_anchor(this->gobj(), - GTK_LAYER_SHELL_EDGE_RIGHT, true); - gtk_layer_auto_exclusive_zone_enable(this->gobj()); - } - - 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 && WaylandDisplay::get().zwf_manager) - { - this->wf_surface = zwf_shell_manager_v2_get_wf_surface( - WaylandDisplay::get().zwf_manager, surface); - } + return parsed_anchor; +} + +void WaylandWindow::init(int width, int height, std::string anchor) +{ + gtk_layer_init_for_window(this->gobj()); + gtk_layer_set_layer(this->gobj(), GTK_LAYER_SHELL_LAYER_OVERLAY); + gtk_layer_set_namespace(this->gobj(), "keyboard"); + gtk_layer_set_exclusive_zone(this->gobj(), -1); + auto layer_anchor = check_anchor(anchor); + if (layer_anchor > -1) + { + gtk_layer_set_anchor(this->gobj(), + (GtkLayerShellEdge)layer_anchor, true); + } else if (layer_anchor == ANCHOR_PINNED_BOTTOM) + { + gtk_layer_set_anchor(this->gobj(), + GTK_LAYER_SHELL_EDGE_BOTTOM, true); + gtk_layer_set_anchor(this->gobj(), + GTK_LAYER_SHELL_EDGE_LEFT, true); + gtk_layer_set_anchor(this->gobj(), + GTK_LAYER_SHELL_EDGE_RIGHT, true); + gtk_layer_auto_exclusive_zone_enable(this->gobj()); } - void WaylandWindow::init_headerbar(int headerbar_size) - { - std::vector buttons = { - &top_button, &bottom_button, &close_button - }; - - const int button_size = 0.8 * headerbar_size; - for (auto& button : buttons) - { - button->get_style_context()->add_class("image-button"); - button->set_size_request(button_size, button_size); - button->set_margin_bottom(OSK_SPACING); - button->set_margin_top(OSK_SPACING); - button->set_margin_left(OSK_SPACING); - button->set_margin_right(OSK_SPACING); - } - - static const std::map gtk_size_map = { - {Gtk::ICON_SIZE_MENU, 16}, - {Gtk::ICON_SIZE_SMALL_TOOLBAR, 16}, - {Gtk::ICON_SIZE_LARGE_TOOLBAR, 24}, - {Gtk::ICON_SIZE_BUTTON, 16}, - {Gtk::ICON_SIZE_DND, 32}, - {Gtk::ICON_SIZE_DIALOG, 48} - }; - - Gtk::BuiltinIconSize desired_gtk_icon_size = Gtk::ICON_SIZE_MENU; - for (auto [gtk_size, pixel_size] : gtk_size_map) - { - if (pixel_size <= button_size) - { - desired_gtk_icon_size = gtk_size; - } - } - - close_button.set_image_from_icon_name("window-close-symbolic", desired_gtk_icon_size); - close_button.signal_clicked().connect_notify([=] () { - this->get_application()->quit(); - }); - - top_button.set_image_from_icon_name("pan-up-symbolic", desired_gtk_icon_size); - top_button.signal_clicked().connect_notify([=] () { - gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, true); - gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, false); - }); - - bottom_button.set_image_from_icon_name("pan-down-symbolic", desired_gtk_icon_size); - bottom_button.signal_clicked().connect_notify([=] () { - gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, false); - gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, true); - }); - - // setup move gesture - Gtk::HeaderBar bar; - headerbar_box.override_background_color(bar.get_style_context()->get_background_color()); - - // setup headerbar layout - headerbar_box.set_size_request(-1, headerbar_size); - headerbar_box.pack_end(close_button, false, false); - headerbar_box.pack_start(top_button, false, false); - headerbar_box.pack_start(bottom_button, false, false); - - layout_box.pack_start(headerbar_box); - layout_box.set_spacing(OSK_SPACING); - this->add(layout_box); + this->set_size_request(width, height); + this->show(); + auto gdk_window = this->get_surface()->gobj(); + auto surface = gdk_wayland_surface_get_wl_surface(gdk_window); + + if (surface && WaylandDisplay::get().zwf_manager) + { + this->wf_surface = zwf_shell_manager_v2_get_wf_surface( + WaylandDisplay::get().zwf_manager, surface); } +} + +void WaylandWindow::init_headerbar(int headerbar_size) +{ + std::vector buttons = { + &top_button, &bottom_button, &close_button + }; - WaylandWindow::WaylandWindow(int width, int height, std::string anchor, int headerbar_size) - : Gtk::Window() + const int button_size = 0.8 * headerbar_size; + for (auto& button : buttons) { - init_headerbar(headerbar_size); - // setup gtk layer shell - init(width, height, anchor); + button->get_style_context()->add_class("image-button"); + button->set_size_request(button_size, button_size); + button->set_margin_bottom(OSK_SPACING); + button->set_margin_top(OSK_SPACING); + button->set_margin_start(OSK_SPACING); + button->set_margin_end(OSK_SPACING); } - void WaylandWindow::set_widget(Gtk::Widget& w) + close_button.set_image_from_icon_name("window-close-symbolic"); + close_button.signal_clicked().connect([=] () { - if (current_widget) - this->layout_box.remove(*current_widget); + this->get_application()->quit(); + }, true); - this->layout_box.pack_end(w); - current_widget = &w; + top_button.set_image_from_icon_name("pan-up-symbolic"); + top_button.signal_clicked().connect([=] () + { + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, true); + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, false); + }, true); + + bottom_button.set_image_from_icon_name("pan-down-symbolic"); + bottom_button.signal_clicked().connect([=] () + { + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, false); + gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, true); + }, true); + + // setup headerbar layout + headerbar_box.set_size_request(-1, headerbar_size); + headerbar_box.append(top_button); + headerbar_box.append(bottom_button); + headerbar_box.append(close_button); + + layout_box.set_orientation(Gtk::Orientation::VERTICAL); + layout_box.append(headerbar_box); + layout_box.set_spacing(OSK_SPACING); + this->set_child(layout_box); +} + +WaylandWindow::WaylandWindow(int width, int height, std::string anchor, int headerbar_size) : + Gtk::Window() +{ + init_headerbar(headerbar_size); + // setup gtk layer shell + init(width, height, anchor); +} - w.set_margin_bottom(OSK_SPACING); - w.set_margin_left(OSK_SPACING); - w.set_margin_right(OSK_SPACING); - this->show_all(); +void WaylandWindow::set_widget(Gtk::Widget& w) +{ + if (current_widget) + { + this->layout_box.remove(*current_widget); } + + this->layout_box.append(w); + current_widget = &w; + + w.set_margin_bottom(OSK_SPACING); + w.set_margin_start(OSK_SPACING); + w.set_margin_end(OSK_SPACING); + this->show(); +} } diff --git a/src/wayland-window.hpp b/src/wayland-window.hpp index 9ee20ca..ebee47a 100644 --- a/src/wayland-window.hpp +++ b/src/wayland-window.hpp @@ -1,9 +1,8 @@ #pragma once -#include +#include #include #include -#include #include #include #include @@ -14,36 +13,36 @@ static constexpr int32_t ANCHOR_PINNED_BOTTOM = -2; namespace wf { - class WaylandDisplay - { - WaylandDisplay(); +class WaylandDisplay +{ + WaylandDisplay(); - public: - static WaylandDisplay& get(); + public: + static WaylandDisplay& get(); - zwf_shell_manager_v2 *zwf_manager = nullptr; - zwp_virtual_keyboard_manager_v1 *vk_manager = nullptr; - }; + zwf_shell_manager_v2 *zwf_manager = nullptr; + zwp_virtual_keyboard_manager_v1 *vk_manager = nullptr; +}; - class WaylandWindow : public Gtk::Window - { - zwf_surface_v2 *wf_surface = nullptr; +class WaylandWindow : public Gtk::Window +{ + zwf_surface_v2 *wf_surface = nullptr; - Gtk::Widget* current_widget = nullptr; - Gtk::Button close_button; - Gtk::Button top_button; - Gtk::Button bottom_button; + Gtk::Widget *current_widget = nullptr; + Gtk::Button close_button; + Gtk::Button top_button; + Gtk::Button bottom_button; - Gtk::HBox headerbar_box; - Gtk::VBox layout_box; + Gtk::Box headerbar_box; + Gtk::Box layout_box; - int32_t check_anchor(std::string anchor); - void init(int width, int height, std::string anchor); - void init_headerbar(int headerbar_size); + int32_t check_anchor(std::string anchor); + void init(int width, int height, std::string anchor); + void init_headerbar(int headerbar_size); - public: - WaylandWindow(int width, int height, std::string anchor, int headerbar_size); - void set_widget(Gtk::Widget& w); - }; + public: + WaylandWindow(int width, int height, std::string anchor, int headerbar_size); + void set_widget(Gtk::Widget& w); +}; }