diff --git a/src/core/lcurses/lcurses.c b/src/core/lcurses/lcurses.c index 20ba479..41f9e2b 100644 --- a/src/core/lcurses/lcurses.c +++ b/src/core/lcurses/lcurses.c @@ -622,7 +622,12 @@ static void register_curses_constants(lua_State *L) CC(KEY_CANCEL) CC(KEY_CLOSE) CC(KEY_COMMAND) CC(KEY_COPY) CC(KEY_CREATE) CC(KEY_END) CC(KEY_EXIT) CC(KEY_FIND) CC(KEY_HELP) - CC(KEY_MARK) CC(KEY_MESSAGE) CC(KEY_MOUSE) + CC(KEY_MARK) CC(KEY_MESSAGE) +#ifndef XCURSES +#ifndef NOMOUSE + CC(KEY_MOUSE) +#endif +#endif CC(KEY_MOVE) CC(KEY_NEXT) CC(KEY_OPEN) CC(KEY_OPTIONS) CC(KEY_PREVIOUS) CC(KEY_REDO) CC(KEY_REFERENCE) CC(KEY_REFRESH) CC(KEY_REPLACE) @@ -645,6 +650,29 @@ static void register_curses_constants(lua_State *L) CC2(KEY_F6, KEY_F(6)) CC2(KEY_F7, KEY_F(7)) CC2(KEY_F8, KEY_F(8)) CC2(KEY_F9, KEY_F(9)) CC2(KEY_F10, KEY_F(10)) CC2(KEY_F11, KEY_F(11)) CC2(KEY_F12, KEY_F(12)) +#ifndef XCURSES +#ifndef NOMOUSE + /* Mouse Constants */ + CC(BUTTON1_RELEASED); CC(BUTTON1_PRESSED); CC(BUTTON1_CLICKED); + CC(BUTTON1_DOUBLE_CLICKED); CC(BUTTON1_TRIPLE_CLICKED); + CC(BUTTON2_RELEASED); CC(BUTTON2_PRESSED); CC(BUTTON2_CLICKED); + CC(BUTTON2_DOUBLE_CLICKED); CC(BUTTON2_TRIPLE_CLICKED); + CC(BUTTON3_RELEASED); CC(BUTTON3_PRESSED); CC(BUTTON3_CLICKED); + CC(BUTTON3_DOUBLE_CLICKED); CC(BUTTON3_TRIPLE_CLICKED); + CC(BUTTON4_RELEASED); CC(BUTTON4_PRESSED); CC(BUTTON4_CLICKED); + CC(BUTTON4_DOUBLE_CLICKED); CC(BUTTON4_TRIPLE_CLICKED); + CC(BUTTON_CTRL); CC(BUTTON_SHIFT); CC(BUTTON_ALT); + CC(REPORT_MOUSE_POSITION); CC(ALL_MOUSE_EVENTS); +#if NCURSES_MOUSE_VERSION > 1 + CC(BUTTON5_RELEASED); CC(BUTTON5_PRESSED); CC(BUTTON5_CLICKED); + CC(BUTTON5_DOUBLE_CLICKED); CC(BUTTON5_TRIPLE_CLICKED); +#else + CC(BUTTON1_RESERVED_EVENT); CC(BUTTON2_RESERVED_EVENT); + CC(BUTTON3_RESERVED_EVENT); CC(BUTTON4_RESERVED_EVENT); +#endif +#endif +#endif + } /* @@ -730,6 +758,75 @@ static int lc_stdscr(lua_State *L) LC_NUMBER2(COLS, COLS) LC_NUMBER2(LINES, LINES) +/* +** ======================================================= +** mouse +** ======================================================= +*/ +#ifndef XCURSES +#ifndef NOMOUSE +static int +lc_ungetmouse(lua_State *L) +{ + MEVENT e; + e.bstate = luaL_checklong(L, 1); + e.x = luaL_checkint(L, 2); + e.y = luaL_checkint(L, 3); + e.z = luaL_checkint(L, 4); + e.id = luaL_checkint(L, 5); + + lua_pushboolean(L, !(!ungetmouse(&e))); + return 1; +} + +static int +lc_getmouse(lua_State *L) +{ + MEVENT e; + if (getmouse(&e) == OK) + { + lua_pushinteger(L, e.bstate); + lua_pushinteger(L, e.x); + lua_pushinteger(L, e.y); + lua_pushinteger(L, e.z); + lua_pushinteger(L, e.id); + return 5; + } + + lua_pushnil(L); + return 1; +} + +static int +lc_mousemask(lua_State *L) +{ + mmask_t m = luaL_checkint(L, 1); + mmask_t om; + m = mousemask(m, &om); + lua_pushinteger(L, m); + lua_pushinteger(L, om); + return 2; +} + +static int +lc_mouseinterval(lua_State *L) +{ + if (!lua_gettop(L)) + lua_pushinteger(L, mouseinterval(-1)); + else + lua_pushinteger(L, mouseinterval(luaL_checkint(L, 1))); + return 1; +} + +static int +lc_has_mouse(lua_State *L) +{ + lua_pushboolean(L, has_mouse()); + return 1; +} +#endif +#endif + /* ** ======================================================= ** color @@ -2314,6 +2411,16 @@ static const luaL_Reg curseslib[] = /* outopts */ { "nl", lc_nl }, +#ifndef XCURSES +#ifndef NOMOUSE + { "mousemask", lc_mousemask }, + { "mouseinterval", lc_mouseinterval}, + { "has_mouse", lc_has_mouse }, + { "getmouse", lc_getmouse }, + { "ungetmouse", lc_ungetmouse }, +#endif +#endif + /* slk */ ECF(slk_init) ECF(slk_set) diff --git a/src/ltui/event.lua b/src/ltui/event.lua index b9eac9b..f98d43d 100644 --- a/src/ltui/event.lua +++ b/src/ltui/event.lua @@ -76,6 +76,14 @@ event:register("cm_max", "cm_quit", "cm_exit", "cm_enter") -- event.keyboard = object {_init = { "key_code", "key_name", "key_meta" }, type = event.ev_keyboard} +-- define mouse event +-- +-- btn_name = button number and event type +-- btn_code = mouse event code +-- x, y = coordinates +-- +event.mouse = object {_init = { "btn_code", "x", "y", "btn_name" }, type = event.ev_mouse} + -- define command event event.command = object {_init = { "command", "extra" }, type = event.ev_command} diff --git a/src/ltui/program.lua b/src/ltui/program.lua index cfb1cc2..7d0d889 100644 --- a/src/ltui/program.lua +++ b/src/ltui/program.lua @@ -44,6 +44,11 @@ function program:init(name, argv) -- disable newline curses.nl(false) + -- init mouse support + if curses.KEY_MOUSE then + curses.mousemask(curses.ALL_MOUSE_EVENTS) + end + -- to filter characters being output to the screen -- this will filter all characters where a chtype or chstr is used curses.map_output(true) @@ -135,6 +140,11 @@ function program:event() -- get input key local key_code, key_name, key_meta = self:_input_key() if key_code then + if curses.KEY_MOUSE and key_code == curses.KEY_MOUSE then + local code, x, y = curses.getmouse() + local name = self:_mouse_map()[code] + return event.mouse{code, x, y, name} + end return event.keyboard{key_code, key_name, key_meta} end end @@ -333,6 +343,20 @@ function program:_key_map() return self._KEYMAP end +-- get mouse map +function program:_mouse_map() + if not self._MOUSEMAP then + -- must be defined dynamically since it depends + -- on curses implementation + self._MOUSEMAP = {} + for n, v in pairs(curses) do + if (n:match('MOUSE') and n ~= 'KEY_MOUSE') or n:match('BUTTON') then + self._MOUSEMAP[v] = n + end + end + end + return self._MOUSEMAP +end -- get input key function program:_input_key() @@ -399,6 +423,7 @@ function program:_input_key() end -- return key info + return ch, key_name, alt end diff --git a/tests/events.lua b/tests/events.lua new file mode 100644 index 0000000..55b616f --- /dev/null +++ b/tests/events.lua @@ -0,0 +1,91 @@ +--!A cross-platform terminal ui library based on Lua +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2020, TBOOX Open Source Group. +-- +-- @author Lael N. Santos +-- @file events.lua +-- +require("tests/load") + +-- requires +local ltui = require("ltui") +local application = ltui.application +local event = ltui.event +local rect = ltui.rect +local window = ltui.window +local label = ltui.label + +-- the demo application +local demo = application() + +-- init demo +function demo:init() + + -- init name + application.init(self, "demo") + + -- init background + self:background_set("black") + + -- init body window + self:insert(self:body_window()) + + -- init teste + self:body_window():panel():insert(self:teste()) +end + +-- get body window +function demo:body_window() + if not self._BODY_WINDOW then + self._BODY_WINDOW = window:new("window.body", rect {1, 1, self:width() - 1, self:height() - 1}, "main window") + end + return self._BODY_WINDOW +end + +-- get teste label +function demo:teste() + if not self._TESTE then + self._TESTE = label:new('demo.label', rect {0, 0, 40, 5}, 'this is a test') + end + return self._TESTE +end + +-- on resize +function demo:on_resize() + self:body_window():bounds_set(rect {1, 1, self:width() - 1, self:height() - 1}) + application.on_resize(self) +end + +-- on event +function demo:on_event(e) + if e.type < event.ev_max then + self:teste():text_set('type: ' .. + tostring(e.type) .. + '; name: ' .. + tostring(e.key_name or e.btn_name) .. + '; code: ' .. + tostring(e.key_code or e.x) .. + '; meta: ' .. + tostring(e.key_code or e.y)) + end + application.on_event(self, e) +end + +-- run demo +demo:run()