Merge pull request #15 from laelnasan/mouse_support

Mouse support
This commit is contained in:
ruki
2020-10-06 10:57:50 +08:00
committed by GitHub
4 changed files with 232 additions and 1 deletions

View File

@@ -622,7 +622,12 @@ static void register_curses_constants(lua_State *L)
CC(KEY_CANCEL) CC(KEY_CLOSE) CC(KEY_COMMAND) CC(KEY_CANCEL) CC(KEY_CLOSE) CC(KEY_COMMAND)
CC(KEY_COPY) CC(KEY_CREATE) CC(KEY_END) CC(KEY_COPY) CC(KEY_CREATE) CC(KEY_END)
CC(KEY_EXIT) CC(KEY_FIND) CC(KEY_HELP) 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_MOVE) CC(KEY_NEXT) CC(KEY_OPEN)
CC(KEY_OPTIONS) CC(KEY_PREVIOUS) CC(KEY_REDO) CC(KEY_OPTIONS) CC(KEY_PREVIOUS) CC(KEY_REDO)
CC(KEY_REFERENCE) CC(KEY_REFRESH) CC(KEY_REPLACE) 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_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_F9, KEY_F(9)) CC2(KEY_F10, KEY_F(10)) CC2(KEY_F11, KEY_F(11))
CC2(KEY_F12, KEY_F(12)) 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(COLS, COLS)
LC_NUMBER2(LINES, LINES) 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 ** color
@@ -2314,6 +2411,16 @@ static const luaL_Reg curseslib[] =
/* outopts */ /* outopts */
{ "nl", lc_nl }, { "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 */ /* slk */
ECF(slk_init) ECF(slk_init)
ECF(slk_set) ECF(slk_set)

View File

@@ -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} 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 -- define command event
event.command = object {_init = { "command", "extra" }, type = event.ev_command} event.command = object {_init = { "command", "extra" }, type = event.ev_command}

View File

@@ -44,6 +44,11 @@ function program:init(name, argv)
-- disable newline -- disable newline
curses.nl(false) 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 -- to filter characters being output to the screen
-- this will filter all characters where a chtype or chstr is used -- this will filter all characters where a chtype or chstr is used
curses.map_output(true) curses.map_output(true)
@@ -135,6 +140,11 @@ function program:event()
-- get input key -- get input key
local key_code, key_name, key_meta = self:_input_key() local key_code, key_name, key_meta = self:_input_key()
if key_code then 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} return event.keyboard{key_code, key_name, key_meta}
end end
end end
@@ -333,6 +343,20 @@ function program:_key_map()
return self._KEYMAP return self._KEYMAP
end 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 -- get input key
function program:_input_key() function program:_input_key()
@@ -399,6 +423,7 @@ function program:_input_key()
end end
-- return key info -- return key info
return ch, key_name, alt return ch, key_name, alt
end end

91
tests/events.lua Normal file
View File

@@ -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()