diff --git a/electron/main.js b/electron/main.js index 6f720329..c1963c3b 100644 --- a/electron/main.js +++ b/electron/main.js @@ -123,6 +123,7 @@ function eventToWindow(event) { } ipcMain.on('window-ops', (event, command, arg) => { + let win; switch (command) { case 'new-window': if (! arg.width || ! arg.height) { @@ -143,25 +144,45 @@ ipcMain.on('window-ops', (event, command, arg) => { case 'toggle-devtools': eventToWindow(event).toggleDevTools(); break; + case 'fullscreen': + win = eventToWindow(event); + arg = arg === 'toggle' ? ! win.isFullScreen() + : arg && arg !== 'false' && arg !== 'off' && arg !== 'no'; + win.setFullScreen(arg == 'toggle' ? ! win.isFullScreen() : arg); + break; case 'set-menubar-visibility': eventToWindow(event).setMenuBarVisibility(arg); break; } }); -ipcMain.on('show-context-menu', (event, items, options) => { - items = items.map((item) => { +function fixMenuItems(items, win = null) { + return items.map((item) => { const clickClientAction = item.clickClientAction; if (clickClientAction) { - item.click = function() { - event.sender.send('do-named-command', clickClientAction); + item.click = function(menuItem, browserWindow, event) { + browserWindow.send('do-named-command', clickClientAction); }; item.clickClientAction = undefined; } + if (win && item.visible === false && item.label == "Exit full screen") + item.visible = win.isFullScreen() + if (item.accelerator && item.accelerator.indexOf(' ') >= 0) + item.accelerator = undefined; + if (item.submenu) + item.submenu = fixMenuItems(item.submenu, win); return item; }); - const menu = Menu.buildFromTemplate(items); - let oarg = {window: BrowserWindow.fromWebContents(event.sender)}; +} + +ipcMain.on('set-application-menu', (event, template) => { + Menu.setApplicationMenu(Menu.buildFromTemplate(fixMenuItems(template))); +}); + +ipcMain.on('show-context-menu', (event, items, options) => { + let win = BrowserWindow.fromWebContents(event.sender); + const menu = Menu.buildFromTemplate(fixMenuItems(items, win)); + let oarg = {window: win }; if (options.x !== undefined && options.y !== undefined) { oarg.x = Math.round(options.x); oarg.y = Math.round(options.y); diff --git a/electron/preload.js b/electron/preload.js index 51d9c68b..265d6c75 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -2,13 +2,10 @@ const _electron = require('electron'); const _fs = require('fs'); const _require = require; - const remote = _electron.remote; const _electronAccess = { clipboard: _electron.clipboard, fs: _fs, ipcRenderer: _electron.ipcRenderer, - Menu: remote.Menu, - MenuItem: remote.MenuItem, require: _require } process.once('loaded', () => { diff --git a/hlib/commands.js b/hlib/commands.js index 461d45d4..09015f3b 100644 --- a/hlib/commands.js +++ b/hlib/commands.js @@ -143,12 +143,20 @@ cmd('enter-mux-mode', dt.enterMuxMode(); return true; }); +cmd('toggle-menubar', + function(dt, key) { + if (DomTerm.toggleMenubar) + DomTerm.toggleMenubar(); + return true; + }); cmd('toggle-fullscreen', function(dt, key) { - if (screenfull.isFullscreen) - screenfull.exit(); - else - screenfull.request(); + DomTerm.windowOp('fullscreen', 'toggle'); + return true; + }); +cmd('exit-fullscreen', + function(dt, key) { + DomTerm.windowOp('fullscreen', 'off'); return true; }); cmd('toggle-fullscreen-current-window', diff --git a/hlib/domterm-client.js b/hlib/domterm-client.js index 665d30f9..dc3aac5f 100644 --- a/hlib/domterm-client.js +++ b/hlib/domterm-client.js @@ -319,8 +319,8 @@ function loadHandler(event) { */ } if (! DomTerm.useIFrame || ! DomTerm.isInIFrame()) - if (DomTerm.setContextMenu && ! DomTerm.simpleLayout) - DomTerm.setContextMenu(); + if (DomTerm.createMenus && ! DomTerm.simpleLayout) + DomTerm.createMenus(); m = location.hash.match(/open=([^&;]*)/); var open_encoded = m ? decodeURIComponent(m[1]) : null; if (open_encoded) { diff --git a/hlib/domterm-menus.js b/hlib/domterm-menus.js index 190fb128..50dc136c 100644 --- a/hlib/domterm-menus.js +++ b/hlib/domterm-menus.js @@ -1,8 +1,3 @@ -//var { Menu, MenuItem} = require("./mwjs-menu-browser.js"); -//import { Menu, MenuItem} from "./mwjs-menu-browser.js"; - -DomTerm.savedMenuBar = null; - DomTerm.aboutMessageVariant = function() { if (DomTerm.isElectron()) { return ' This variant of DomTerm uses Electron ' @@ -43,10 +38,14 @@ DomTerm.showAboutMessage = function() { } DomTerm.createMenus = function(options) { - let platform = options.platform; - let menuItem = options.menuItem; // DomTerm.makeMenuItem; - let popup = options.popup; - let Menu = options.Menu; + let platform; + if (DomTerm.isElectron() && ! DomTerm.usingJsMenus() && ! DomTerm.isAtom()) + platform = "electron"; + else if (! DomTerm.isAtom() && ! DomTerm.usingQtWebEngine) + platform = "generic"; + else + return; + let menuItem = DomTerm.makeMenuItem; let isElectron = DomTerm.isElectron(); let electronMenus = platform == "electron"; @@ -54,7 +53,7 @@ DomTerm.createMenus = function(options) { if (electronMenus) electronAccess.ipcRenderer.send('window-ops', 'set-menubar-visibility', show); else - Menu.setApplicationMenu(show ? DomTerm.savedMenuBar : null); + Menu.setApplicationMenu(show ? DomTerm._savedMenuBar : null); } const muxPrefix = 'CommandOrControl+Shift+M'; const copyItem = @@ -69,11 +68,12 @@ DomTerm.createMenus = function(options) { var showingMenuBar = true; const showMenuBarItem = menuItem({label: 'Show menubar', type: 'checkbox', - click: function() { - showingMenuBar = ! showingMenuBar; - showMenubar(showingMenuBar); - }, + clickClientAction: 'toggle-menubar', checked: true}); + DomTerm.toggleMenubar = function() { + showingMenuBar = ! showingMenuBar; + showMenubar(showingMenuBar); + } const autoPagingItem = menuItem({label: 'Automatic Pager', accelerator: "Ctrl+Shift+M A", type: 'checkbox', @@ -90,10 +90,8 @@ DomTerm.createMenus = function(options) { accelerator: 'Ctrl+Shift+L', clickClientAction: 'input-mode-cycle'}); - const inputMenu = new Menu(); - inputMenu.append(charModeItem); - inputMenu.append(lineModeItem); - inputMenu.append(autoModeItem); + const inputMenu = DomTerm.makeMenu([ + charModeItem, lineModeItem, autoModeItem ]); const inputModeMenu = menuItem({label: 'Input mode', submenu: inputMenu}); const saveAsItem = menuItem({label: 'Save as HTML', @@ -111,22 +109,23 @@ DomTerm.createMenus = function(options) { const newPaneItem = menuItem({label: 'New terminal (right/below)', accelerator: 'Ctrl+Shift+A Enter', clickClientAction: 'new-pane'}); - const newTerminalMenu = new Menu(); - newTerminalMenu.append(newWindowItem); - newTerminalMenu.append(newTabItem); - newTerminalMenu.append(newPaneItem); - newTerminalMenu.append(menuItem({label: 'New terminal above', - accelerator: 'Ctrl+Shift+A Ctrl+Up', - clickClientAction: 'new-pane-above'})); - newTerminalMenu.append(menuItem({label: 'New terminal below', - accelerator: 'Ctrl+Shift+A Ctrl+Down', - clickClientAction: 'new-pane-below'})); - newTerminalMenu.append(menuItem({label: 'New terminal left', - accelerator: 'Ctrl+Shift+A Ctrl+Left', - clickClientAction: 'new-pane-left'})); - newTerminalMenu.append(menuItem({label: 'New terminal right', - accelerator: 'Ctrl+Shift+A Ctrl+Right', - clickClientAction: 'new-pane-right'})); + const newTerminalMenu = DomTerm.makeMenu([ + newWindowItem, + newTabItem, + newPaneItem, + menuItem({label: 'New terminal above', + accelerator: 'Ctrl+Shift+A Ctrl+Up', + clickClientAction: 'new-pane-above'}), + menuItem({label: 'New terminal below', + accelerator: 'Ctrl+Shift+A Ctrl+Down', + clickClientAction: 'new-pane-below'}), + menuItem({label: 'New terminal left', + accelerator: 'Ctrl+Shift+A Ctrl+Left', + clickClientAction: 'new-pane-left'}), + menuItem({label: 'New terminal right', + accelerator: 'Ctrl+Shift+A Ctrl+Right', + clickClientAction: 'new-pane-right'}) + ]); const newTerminalMenuItem = menuItem({label: 'New Terminal', submenu: newTerminalMenu}); const detachMenuItem = @@ -148,52 +147,59 @@ DomTerm.createMenus = function(options) { menuItem({label: 'Copy', accelerator: DomTerm.isMac ? 'Cmd+C' : 'Ctrl+Shift+C', click() { DomTerm.doContextCopy(); }}); const copyLinkSep = menuItem({type: 'separator'}); - - const contextMenu = new Menu(); - contextMenu.append(showMenuBarItem); - contextMenu.append(copyItem); - contextMenu.append(pasteItem); - contextMenu.append(inputModeMenu); - contextMenu.append(autoPagingItem); - contextMenu.append(newTerminalMenuItem); - contextMenu.append(detachMenuItem); - const contextLinkMenu = new Menu(); - contextLinkMenu.append(openLinkItem); - contextLinkMenu.append(copyLinkItem); - contextLinkMenu.append(copyLinkSep); - contextLinkMenu.append(showMenuBarItem); - contextLinkMenu.append(copyLinkTextItem); // not plain copyItem - contextLinkMenu.append(pasteItem); - contextLinkMenu.append(inputModeMenu); - contextLinkMenu.append(autoPagingItem); - contextLinkMenu.append(newTerminalMenuItem); - contextLinkMenu.append(detachMenuItem); + const fullscreenExitItem = + // Note that electron/main.js checks for this specific label. + menuItem({label: "Exit full screen", + visible: false, // hidden unless fullscreen + clickClientAction: 'exit-fullscreen'}); + const contextMenu = DomTerm.makeMenu([ + showMenuBarItem, + copyItem, + pasteItem, + inputModeMenu, + autoPagingItem, + newTerminalMenuItem, + detachMenuItem, + fullscreenExitItem + ]); + const contextLinkMenu = DomTerm.makeMenu([ + openLinkItem, + copyLinkItem, + copyLinkSep, + showMenuBarItem, + copyLinkTextItem, // not plain copyItem + pasteItem, + inputModeMenu, + autoPagingItem, + newTerminalMenuItem, + detachMenuItem, + fullscreenExitItem + ]); const showInspectorItem = ! window._dt_toggleDeveloperTools ? null : menuItem({label: 'Toggle Developer Tools', accelerator: 'Ctrl+Shift+I', clickClientAction: 'toggle-developer-tools'}); - let fileMenu = new Menu(); - fileMenu.append(newWindowItem); - fileMenu.append(newTabItem); - fileMenu.append(saveAsItem); - fileMenu.append(quitItem); - //let fileMenuItem = menuItem({label: 'File', submenu: fileMenu}); - let editMenu = new Menu(); - editMenu.append(copyItem); - editMenu.append(copyAsHtmlItem); - editMenu.append(pasteItem); - editMenu.append(menuItem({label: 'Clear Buffer', - clickClientAction: 'clear-buffer'})); - editMenu.append(menuItem({label: 'Find', - accelerator: 'Ctrl+Shift+F', - clickClientAction: 'find-text'})); - let viewMenu = new Menu(); - viewMenu.append(showMenuBarItem); - + let fileMenu = DomTerm.makeMenu([ + newWindowItem, + newTabItem, + saveAsItem, + quitItem + ]); + let editMenu = DomTerm.makeMenu([ + copyItem, + copyAsHtmlItem, + pasteItem, + menuItem({label: 'Clear Buffer', + clickClientAction: 'clear-buffer'}), + menuItem({label: 'Find', + accelerator: 'Ctrl+Shift+F', + clickClientAction: 'find-text'}) + ]); + let viewMenuItems = []; + viewMenuItems.push(showMenuBarItem); if (electronMenus) { - viewMenu.append(menuItem({role: 'togglefullscreen'})); + viewMenuItems.push(menuItem({role: 'togglefullscreen'})); } else if (typeof screenfull !== "undefined") { - let fullscreenExitItem; let fullscreenAllItem = menuItem({label: "Full screen (all)", type: 'checkbox', accelerator: 'F11', @@ -202,13 +208,6 @@ DomTerm.createMenus = function(options) { menuItem({label: "Full screen (current)", type: 'checkbox', accelerator: 'Shift-F11', clickClientAction: 'toggle-fullscreen-current-window'}); - fullscreenExitItem = menuItem({label: "Exit full screen", - visible: false, // hidden unless fullscreen - click: function() { - if (screenfull.isFullscreen) - screenfull.exit(); - fullscreenExitItem.visible = false; - }}); if (screenfull.isEnabled) { screenfull.on('change', () => { fullscreenAllItem.checked = false; @@ -227,43 +226,45 @@ DomTerm.createMenus = function(options) { showMenuBarItem.enabled = ! fullscreenCurrentItem.checked; }); } - viewMenu.append(fullscreenAllItem); - viewMenu.append(fullscreenCurrentItem); - contextMenu.append(fullscreenExitItem); + viewMenuItems.push(fullscreenAllItem); + viewMenuItems.push(fullscreenCurrentItem); } - if (electronMenus) { - viewMenu.append(menuItem({type: 'separator'})); - viewMenu.append(menuItem({role: 'resetzoom'})); - viewMenu.append(menuItem({role: 'zoomin'})); - viewMenu.append(menuItem({role: 'zoomout'})); + viewMenuItems.push(menuItem({type: 'separator'})); + viewMenuItems.push(menuItem({role: 'resetzoom'})); + viewMenuItems.push(menuItem({role: 'zoomin'})); + viewMenuItems.push(menuItem({role: 'zoomout'})); } if (showInspectorItem != null) { - viewMenu.append(menuItem({type: 'separator'})); - viewMenu.append(showInspectorItem); + viewMenuItems.push(menuItem({type: 'separator'})); + viewMenuItems.push(showInspectorItem); + } + let terminalMenu = DomTerm.makeMenu([ + cycleInputModesItem, + newTerminalMenuItem, + detachMenuItem, + resetMenuItem + ]); + let helpMenu = DomTerm.makeMenu([ + aboutItem, homePageItem + ]); + let menuBarItems = [ + menuItem({label: 'File', submenu: fileMenu}), + menuItem({label: 'Edit', submenu: editMenu}), + menuItem({label: 'View', submenu: DomTerm.makeMenu(viewMenuItems)}), + menuItem({label: 'Terminal', submenu: terminalMenu}), + menuItem({label: 'Help', submenu: helpMenu}) + ]; + let menuBar; + if (electronMenus) { + electronAccess.ipcRenderer.send('set-application-menu', menuBarItems); + } else { + menuBar = new Menu({ type: 'menubar' }, menuBarItems); + if (isElectron) + electronAccess.ipcRenderer.send('window-ops', 'set-menubar-visibility', false); + DomTerm._savedMenuBar = menuBar; + Menu.setApplicationMenu(menuBar); } - let terminalMenu = new Menu(); - terminalMenu.append(cycleInputModesItem); - terminalMenu.append(newTerminalMenuItem); - terminalMenu.append(detachMenuItem); - terminalMenu.append(resetMenuItem); - let helpMenu = new Menu(); - helpMenu.append(aboutItem); - if (homePageItem != null) - helpMenu.append(homePageItem); - - let menuBar = new Menu({ type: 'menubar' }); - menuBar.append(menuItem({label: 'File', submenu: fileMenu})); - menuBar.append(menuItem({label: 'Edit', submenu: editMenu})); - menuBar.append(menuItem({label: 'View', submenu: viewMenu})); - menuBar.append(menuItem({label: 'Terminal', submenu: terminalMenu})); - menuBar.append(menuItem({label: 'Help', submenu: helpMenu})); - if (electronMenus) - menuBar = Menu.buildFromTemplate(menuBar.items); - else if (isElectron) - electronAccess.ipcRenderer.send('window-ops', 'set-menubar-visibility', false); - Menu.setApplicationMenu(menuBar); - DomTerm.savedMenuBar = menuBar; DomTerm.showContextMenu = function(options) { DomTerm._contextOptions = options; @@ -273,11 +274,13 @@ DomTerm.createMenus = function(options) { lineModeItem.checked = mode == 108; autoModeItem.checked = mode == 97; } + showMenuBarItem.checked = showingMenuBar; autoModeItem.visible = DomTerm.supportsAutoInputMode; + fullscreenExitItem.visible = screenfull.isFullscreen; if (options.autoPaging !== undefined) autoPagingItem.checked = options.autoPaging; let cmenu = options.contextType=="A" ? contextLinkMenu : contextMenu; - popup(cmenu, options); + DomTerm.popupMenu(cmenu, options); return true; }; } @@ -288,28 +291,25 @@ if (DomTerm.isElectron() ) { }); } -DomTerm.popupMenu = function(items, options) { +DomTerm.popupMenu = function(menu, options) { if (DomTerm.isElectron() && ! DomTerm.usingJsMenus() && ! DomTerm.isAtom()) { - electronAccess.ipcRenderer.send("show-context-menu", items, options); + electronAccess.ipcRenderer.send("show-context-menu", menu, options); } else if (false && DomTerm.usingQtWebEngine) { // TODO } else if (! DomTerm.isAtom()) { - let menu = DomTerm.makeMenu(items); let x = options.x || options.clientX || 0; let y = options.y || options.clientY || 0; - //if (menu.node) - // menu.popdown(); + if (menu.node) + menu.popdown(); menu.popup(x, y); + let dt = DomTerm.focusedTerm; + if (dt) + dt.maybeFocus(true); } } DomTerm.makeMenu = function(items) { if (DomTerm.isElectron() && ! DomTerm.usingJsMenus() && ! DomTerm.isAtom()) { - const {Menu, MenuItem} = electronAccess; - let menu = new Menu(); - for (item of items) { - menu.append(DomTerm.makeMenuItem(item)); - } - return menu; + return items; } else if (false && DomTerm.usingQtWebEngine) { // TODO } else if (! DomTerm.isAtom()) { @@ -322,23 +322,13 @@ DomTerm.makeMenu = function(items) { return null; } DomTerm.makeMenuItem = function(options) { + if (options instanceof MenuItem) + return options; if (DomTerm._makeMenuItem) return DomTerm._makeMenuItem(options); if (DomTerm.isElectron() && ! DomTerm.usingJsMenus() && ! DomTerm.isAtom()) { - const {Menu, MenuItem} = electronAccess; function menuItem(options) { - if (options && options.accelerator - && options.accelerator.indexOf(' ') >= 0) - options.accelerator = undefined; - const clickClientAction = options && options.clickClientAction; - if (clickClientAction) { - // FIXME FUTURE Handle in main.js, to avoid need for "remote" - options.click = function() { - DomTerm.doNamedCommand(clickClientAction); - }; - options.clickClientAction = undefined; - } - return new MenuItem(options); + return options; } DomTerm._makeMenuItem = menuItem; return menuItem(options); @@ -360,33 +350,3 @@ DomTerm.makeMenuItem = function(options) { } return null; // ERROR } - -DomTerm.setContextMenu = function() { - if (DomTerm.isElectron() && ! DomTerm.usingJsMenus() && ! DomTerm.isAtom()) { - const {Menu, MenuItem} = electronAccess; - function popup(cmenu, options) { - cmenu.popup(options); - } - DomTerm.createMenus({platform: "electron", - popup: popup, - menuItem: DomTerm.makeMenuItem, - Menu: Menu - }); - } else if (! DomTerm.isAtom() && ! DomTerm.usingQtWebEngine) { - function popup(cmenu, options) { - let clientX = options.x || options.clientX || 0; - let clientY = options.y || options.clientY || 0; - if (cmenu.node) - cmenu.popdown(); - cmenu.popup(clientX, clientY); - let dt = DomTerm.focusedTerm; - if (dt) - dt.maybeFocus(true); - } - DomTerm.createMenus({platform: "generic", - popup: popup, - menuItem: DomTerm.makeMenuItem, - Menu: Menu - }); - } -} diff --git a/hlib/domterm-parser.js b/hlib/domterm-parser.js index 21f34038..ce5a9098 100644 --- a/hlib/domterm-parser.js +++ b/hlib/domterm-parser.js @@ -1952,6 +1952,11 @@ class DTParser { case 'detach': term.detachSession(); break; + case 'fullscreen on': + case 'fullscreen off': + case 'fullscreen toggle': + DomTerm.windowOp('fullscreen', text.substring(11)); + break; } break; case 102: diff --git a/hlib/domterm.js b/hlib/domterm.js index feff8fe1..37438a2b 100644 --- a/hlib/domterm.js +++ b/hlib/domterm.js @@ -170,10 +170,24 @@ DomTerm.windowClose = function() { window.close(); } -// 'hide', 'show', 'minimize' -DomTerm.windowOp = function(opname) { +// 'hide', 'show', 'minimize', 'fullscreen' +DomTerm.windowOp = function(opname, arg=null) { + if (opname === 'fullscreen') { + // arg must be 'on', 'off', 'toogle' (or null) + let current = screenfull.isFullscreen; + let goal = arg===null || arg === 'toggle' ? ! current + : arg && arg !== 'off'; + if (goal !== current) { + if (! DomTerm.isElectron()) { + if (current) + screenfull.exit(); + else + screenfull.request(); + } + } + } if (DomTerm.isElectron()) { - electronAccess.ipcRenderer.send('window-ops', opname, null); + electronAccess.ipcRenderer.send('window-ops', opname, arg); } } diff --git a/lws-term/commands.cc b/lws-term/commands.cc index 302df9cc..e4b6c467 100644 --- a/lws-term/commands.cc +++ b/lws-term/commands.cc @@ -819,14 +819,8 @@ int reverse_video_action(int argc, arglist_t argv, struct lws *wsi, return EXIT_FAILURE; } const char *opt = argc < 2 ? "on" : argv[1]; - bool on; - if (strcasecmp(opt, "on") == 0 || strcasecmp(opt, "yes") == 0 - || strcasecmp(opt, "true") == 0) - on = true; - else if (strcasecmp(opt, "off") == 0 || strcasecmp(opt, "no") == 0 - || strcasecmp(opt, "false") == 0) - on = false; - else { + int on = bool_value(opt); + if (on < 0) { printf_error(opts, "arguments to reverse-video is not on/off/yes/no/true/false"); return EXIT_FAILURE; @@ -972,6 +966,22 @@ int window_action(int argc, arglist_t argv, struct lws *wsi, w_op_kind = w_simple; //default_windows = "current"; seq = URGENT_WRAP("\033]97;detach\007"); + } else if (strcmp(subcommand, "fullscreen") == 0) { + const char *subarg = argc >= i+1 ? argv[i+1] : NULL; + w_op_kind = w_simple; + default_windows = "top"; + int bval; + if (subarg == NULL || strcmp(subarg, "toggle") == 0) + seq = URGENT_WRAP("\033]97;fullscreen toggle\007"); + else if ((bval = bool_value(subarg)) == 1) + seq = URGENT_WRAP("\033]97;fullscreen on\007"); + else if (bval == 0) + seq = URGENT_WRAP("\033]97;fullscreen off\007"); + else { + printf_error(opts, "invalid value '%s' to 'window fullscreen' command", + subarg); + return EXIT_FAILURE; + } } if (w_op_kind == w_none) { printf_error(opts, diff --git a/lws-term/utils.cc b/lws-term/utils.cc index 0224c994..0bca9095 100644 --- a/lws-term/utils.cc +++ b/lws-term/utils.cc @@ -996,3 +996,15 @@ const char *get_clipboard_command(const char *op, bool clear_cache) return strcmp(op, "paste") == 0 ? read_clip_cmd : read_sel_cmd; } } + +/** Return 0 or 1 for a valid boolean value; -1 otherwise. */ +int bool_value(const char *value) +{ + if (strcasecmp(value, "on") == 0 || strcasecmp(value, "yes") == 0 + || strcasecmp(value, "true") == 0) + return 1; + if (strcasecmp(value, "off") == 0 || strcasecmp(value, "no") == 0 + || strcasecmp(value, "false") == 0) + return 0; + return -1; +} diff --git a/lws-term/utils.h b/lws-term/utils.h index 8976e01a..460c342a 100644 --- a/lws-term/utils.h +++ b/lws-term/utils.h @@ -94,5 +94,6 @@ extern int popen_read(const char *command, sbuf& sb); extern const char *get_clipboard_command(const char *op, bool clear_cache=false); extern char*find_in_path(const char*); extern bool have_in_path(const char*); +extern int bool_value(const char*); #endif //TTYD_UTIL_H