[mice] support for pure motion events

Deprecate notcurses_mouse_{enable, disable}. Reimplement
them for now as wrappers around notcurses_mice_enable().
New function notcurses_mice_disable() is a static inline
wrapper around notcurses_mice_enable(). The latter
function takes an unsigned bitmask of event types. We
now turn on the "all motion" tracking DECSET if
NCMICE_MOVE_EVENT is requested. Update the documentation,
and kill some obsolete lines. Add the ypx and xpx fields
to ncinput, to indicate pixel offset within a cell. Add
nckey NCKEY_MOTION for button-free motion events. Update
notcurses-input to pass NCMICE_ALL_EVENTS and decode
NCKEY_MOTION. Only emit mouse sequences when connected
to a TTY (or GPM). Closes #2320.

Request RGB XTGETTCAP.

Fix bug in error check in notcurses_render_to_buffer().

Decode multiple XTGETTCAP responses.
This commit is contained in:
nick black 2021-11-05 08:32:35 -04:00
parent 960414d818
commit 0f5aec510a
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC
17 changed files with 240 additions and 163 deletions

View File

@ -12,6 +12,15 @@ rearrangements of Notcurses.
deprecated functionality, ABI3 ought require small changes, if any.
* 2.4.9 (not yet released)
* `notcurses_mice_enable()` and `notcurses_mouse_disable()` replace
`notcurses_mouse_enable()` and `notcurses_mouse_disable()`, which
have been deprecated, and will be removed in ABI3.
`notcurses_mice_enable()` takes an additional `unsigned eventmask`
parameter, a bitmask union over `NCMICE_*_EVENT` (`NCMICE_ALL_EVENTS`
is provided for convenience and future-proofing).
`notcurses_mice_disable()` is now a `static inline` wrapper around the
former, passing 0 as the event mask. This can be used to get mouse
movement buttons and focus events, which were previously unavailable.
* The handling of geometry and distance has been normalized across all
functions. Lengths are now `unsigned` as opposed to `int`. Where -1 was
being used to indicate "everything", 0 is now required. This affects

View File

@ -27,7 +27,14 @@ typedef struct ncinput {
EVTYPE_REPEAT,
EVTYPE_RELEASE,
} evtype;
int ypx, xpx; // pixel offsets within cell, -1 for undefined
} ncinput;
#define NCMICE_NO_EVENTS 0
#define NCMICE_MOVE_EVENT 0x1
#define NCMICE_BUTTON_EVENT 0x2
#define NCMICE_DRAG_EVENT 0x4
#define NCMICE_ALL_EVENTS 0x7
```
**bool nckey_mouse_p(uint32_t ***r***);**
@ -42,9 +49,9 @@ typedef struct ncinput {
**uint32_t notcurses_getc_blocking(struct notcurses* ***n***, ncinput* ***ni***);**
**int notcurses_mouse_enable(struct notcurses* ***n***);**
**int notcurses_mice_enable(struct notcurses* ***n***, unsigned ***eventmask***);**
**int notcurses_mouse_disable(struct notcurses* ***n***);**
**int notcurses_mice_disable(struct notcurses* ***n***);**
**int notcurses_inputready_fd(struct notcurses* ***n***);**
@ -92,13 +99,14 @@ action, but signals in the interim are permanently lost.
## Mice
For mouse events, the additional fields **y** and **x** are set. These
fields are not meaningful for keypress events. Mouse events can be
distinguished using the **nckey_mouse_p** predicate. Once enabled, mouse
button presses and releases are detected, as are mouse motions made while a
button is held down. If no button is depressed, mouse movement _does not
result in events_. This is known as "button-event tracking" mode in the
nomenclature of [Xterm Control Sequences](https://www.xfree86.org/current/ctlseqs.html).
For mouse events, the additional fields ***y***, ***x***, ***ypx***, and
***xpx*** are set. These fields are not meaningful for keypress events.
Mouse events can be distinguished using the **nckey_mouse_p** predicate.
**NCMICE_MOVE_EVENT** requests events whenever the mouse moves when no
buttons are held down. **NCMICE_DRAG_EVENT** requests events when the mouse
is moving with buttons held down. **NCMICE_BUTTON_EVENT** requests events
then the button state changes. **NCMICE_ALL_EVENTS** is provided for
convenience and future-proofing against API (though not ABI) changes.
## Synthesized keypresses
@ -144,8 +152,9 @@ If an error is encountered before **notcurses_getvec** has read any input,
it will return -1. If it times out before reading any input, it will return
0. Otherwise, it returns the number of **ncinput** objects written back.
**notcurses_mouse_enable** returns 0 on success, and non-zero on failure, as
does **notcurses_mouse_disable**.
**notcurses_mice_enable** returns 0 on success, and non-zero on failure, as
does **notcurses_mice_disable**. Success does not necessarily mean that a
mouse is available nor that all requested events will be generated.
**ncinput_equal_p** returns **true** if the two **ncinput** structs represent
the same input (though not necessarily the same input event), and
@ -172,34 +181,36 @@ requested. This eliminates most of the **BUGS** mentioned below.
# BUGS
Failed escape sequences are not yet played back in their entirety; only an
ESC (ASCII 0x1b) will be seen by the application.
The Shift key is only indicated in conjunction with mouse button presses. If
e.g. Shift is used to generate a capital letter 'A', **id** will equal 'A',
and **shift** will be **false**. This should be fixed in the future.
When Ctrl is pressed along with a letter, the letter will currently always be
reported in its uppercase form. E.g., if Shift, Ctrl, and 'a' are all pressed,
this is indistinguishable from Ctrl and 'a' without Shift. This should be fixed
in the future.
The Shift key is not indicated in conjunction with typical Unicode text.
If e.g. Shift is used to generate a capital letter 'A', ***id*** will equal 'A',
and ***shift*** will be **false**. Similarly, when Ctrl is pressed along with a
letter, the letter will currently always be reported in its uppercase form.
E.g., if Shift, Ctrl, and 'a' are all pressed, this is indistinguishable from
Ctrl and 'A'.
Ctrl pressed along with 'J' or 'M', whether Shift is pressed or not,
currently registers as **NCKEY_ENTER**. This will likely change in the
future.
When the Kitty keyboard disambiguation protocol is used, most of these
issues are resolved.
issues are resolved. You can determine whether the protocol is in use
by examining the output of **notcurses-info(1)**. If the **kbd** property
is indicated, you're using the Kitty protocol.
Mouse events in the top and left margins will never be delivered to the
application (as is intended), but mouse events in the bottom and right margins
sometimes can be if the event occurs prior to a window resize.
The ***ypx*** and ***xpx*** fields are never currently valid (i.e. they are
always -1). This ought be fixed in the future using the SGR PixelMode mouse
protocol.
On some operating systems, **CLOCK_REALTIME** is used as the basis for
timeouts instead of **CLOCK_MONOTONIC**. This ought be fixed.
# SEE ALSO
**notcurses-info(1)**,
**clock_gettime(2)**,
**poll(2)**,
**notcurses(3)**,

View File

@ -225,14 +225,14 @@ namespace ncpp
return notcurses_palette_size (static_cast<const notcurses*> (nc));
}
bool mouse_enable () const NOEXCEPT_MAYBE
bool mouse_enable (unsigned eventmask) const NOEXCEPT_MAYBE
{
return error_guard (notcurses_mouse_enable (nc), -1);
return error_guard (notcurses_mice_enable (nc, eventmask), -1);
}
bool mouse_disable () const NOEXCEPT_MAYBE
{
return error_guard (notcurses_mouse_disable (nc), -1);
return error_guard (notcurses_mice_disable (nc), -1);
}
CellStyle get_supported_styles () const noexcept

View File

@ -133,9 +133,9 @@ extern "C" {
#define NCKEY_RSUPER suppuabize(180)
#define NCKEY_RHYPER suppuabize(181)
#define NCKEY_RMETA suppuabize(182)
// mouse events. We try to encode some details into the char32_t (i.e. which
// button was pressed), but some is embedded in the ncinput event. the release
// event is generic across buttons; callers must maintain state, if they care.
// mouse events. We encode which button was pressed into the char32_t,
// but position information is embedded in the larger ncinput event.
#define NCKEY_MOTION suppuabize(200) // no buttons pressed
#define NCKEY_BUTTON1 suppuabize(201)
#define NCKEY_BUTTON2 suppuabize(202)
#define NCKEY_BUTTON3 suppuabize(203)

View File

@ -982,41 +982,51 @@ API int notcurses_leave_alternate_screen(struct notcurses* nc)
__attribute__ ((nonnull (1)));
// Return the topmost plane of the pile containing 'n'.
API struct ncplane* ncpile_top(struct ncplane* n);
API struct ncplane* ncpile_top(struct ncplane* n)
__attribute__ ((nonnull (1)));
// Return the bottommost plane of the pile containing 'n'.
API struct ncplane* ncpile_bottom(struct ncplane* n);
API struct ncplane* ncpile_bottom(struct ncplane* n)
__attribute__ ((nonnull (1)));
// Renders the pile of which 'n' is a part. Rendering this pile again will blow
// away the render. To actually write out the render, call ncpile_rasterize().
API int ncpile_render(struct ncplane* n);
API int ncpile_render(struct ncplane* n)
__attribute__ ((nonnull (1)));
// Make the physical screen match the last rendered frame from the pile of
// which 'n' is a part. This is a blocking call. Don't call this before the
// pile has been rendered (doing so will likely result in a blank screen).
API int ncpile_rasterize(struct ncplane* n);
API int ncpile_rasterize(struct ncplane* n)
__attribute__ ((nonnull (1)));
// Renders and rasterizes the standard pile in one shot. Blocking call.
API int notcurses_render(struct notcurses* nc);
API int notcurses_render(struct notcurses* nc)
__attribute__ ((nonnull (1)));
// Perform the rendering and rasterization portion of ncpile_render() and
// ncpile_rasterize(), but do not write the resulting buffer out to the
// terminal. Using this function, the user can control the writeout process.
// The returned buffer must be freed by the caller.
API int ncpile_render_to_buffer(struct ncplane* p, char** buf, size_t* buflen);
API int ncpile_render_to_buffer(struct ncplane* p, char** buf, size_t* buflen)
__attribute__ ((nonnull (1, 2, 3)));
// Write the last rendered frame, in its entirety, to 'fp'. If
// notcurses_render() has not yet been called, nothing will be written.
API int ncpile_render_to_file(struct ncplane* p, FILE* fp);
API int ncpile_render_to_file(struct ncplane* p, FILE* fp)
__attribute__ ((nonnull (1, 2)));
// Return the topmost ncplane of the standard pile.
API struct ncplane* notcurses_top(struct notcurses* n);
API struct ncplane* notcurses_top(struct notcurses* n)
__attribute__ ((nonnull (1)));
// Return the bottommost ncplane of the standard pile.
API struct ncplane* notcurses_bottom(struct notcurses* n);
API struct ncplane* notcurses_bottom(struct notcurses* n)
__attribute__ ((nonnull (1)));
// Destroy all ncplanes other than the stdplane.
API void notcurses_drop_planes(struct notcurses* nc);
API void notcurses_drop_planes(struct notcurses* nc)
__attribute__ ((nonnull (1)));
// All input is taken from stdin. We attempt to read a single UTF8-encoded
// Unicode codepoint, *not* an entire Extended Grapheme Cluster. It is also
@ -1042,7 +1052,7 @@ nckey_supppuab_p(uint32_t w){
// Is the event a synthesized mouse event?
static inline bool
nckey_mouse_p(uint32_t r){
return r >= NCKEY_BUTTON1 && r <= NCKEY_BUTTON11;
return r >= NCKEY_MOTION && r <= NCKEY_BUTTON11;
}
// An input event. Cell coordinates are currently defined only for mouse
@ -1050,8 +1060,7 @@ nckey_mouse_p(uint32_t r){
// ncinput. We encompass single Unicode codepoints, not complete EGCs.
typedef struct ncinput {
uint32_t id; // Unicode codepoint or synthesized NCKEY event
int y; // y cell coordinate of event, -1 for undefined
int x; // x cell coordinate of event, -1 for undefined
int y, x; // y/x cell coordinate of event, -1 for undefined
bool alt; // was alt held?
bool shift; // was shift held?
bool ctrl; // was ctrl held?
@ -1062,6 +1071,7 @@ typedef struct ncinput {
NCTYPE_REPEAT,
NCTYPE_RELEASE,
} evtype;
int ypx, xpx; // pixel offsets within cell, -1 for undefined
} ncinput;
// compare two ncinput structs for data equality.
@ -1106,7 +1116,7 @@ API int notcurses_inputready_fd(struct notcurses* n)
__attribute__ ((nonnull (1)));
// 'ni' may be NULL if the caller is uninterested in event details. If no event
// is ready, returns 0.
// is immediately ready, returns 0.
static inline uint32_t
notcurses_getc_nblock(struct notcurses* n, ncinput* ni){
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
@ -1114,26 +1124,35 @@ notcurses_getc_nblock(struct notcurses* n, ncinput* ni){
}
// 'ni' may be NULL if the caller is uninterested in event details. Blocks
// until an event is processed or a signal is received.
// until an event is processed or a signal is received (including resize events).
static inline uint32_t
notcurses_getc_blocking(struct notcurses* n, ncinput* ni){
return notcurses_get(n, NULL, ni);
}
// Was 'ni' free of modifiers?
static inline bool
ncinput_nomod_p(const ncinput* ni){
return !ni->alt && !ni->ctrl && !ni->shift;
return !(ni->alt | ni->ctrl | ni->shift);
}
// Enable the mouse in "button-event tracking" mode with focus detection and
// UTF8-style extended coordinates. On failure, -1 is returned. On success, 0
// is returned, and mouse events will be published to notcurses_get().
API int notcurses_mouse_enable(struct notcurses* n)
#define NCMICE_NO_EVENTS 0
#define NCMICE_MOVE_EVENT 0x1
#define NCMICE_BUTTON_EVENT 0x2
#define NCMICE_DRAG_EVENT 0x4
#define NCMICE_ALL_EVENTS 0x7
// Enable mice events according to 'eventmask'; an eventmask of 0 will disable
// all mice tracking. On failure, -1 is returned. On success, 0 is returned, and
// mouse events will be published to notcurses_get().
API int notcurses_mice_enable(struct notcurses* n, unsigned eventmask)
__attribute__ ((nonnull (1)));
// Disable mouse events. Any events in the input queue can still be delivered.
API int notcurses_mouse_disable(struct notcurses* n)
__attribute__ ((nonnull (1)));
__attribute__ ((nonnull (1))) static inline int
notcurses_mice_disable(struct notcurses* n){
return notcurses_mice_enable(n, NCMICE_NO_EVENTS);
}
// Disable signals originating from the terminal's line discipline, i.e.
// SIGINT (^C), SIGQUIT (^\), and SIGTSTP (^Z). They are enabled by default.
@ -4234,6 +4253,11 @@ API int ncvisual_blitter_geom(const struct notcurses* nc, const struct ncvisual*
int* scaley, int* scalex, ncblitter_e* blitter)
__attribute__ ((nonnull (1))) __attribute__ ((deprecated));
API int notcurses_mouse_enable(struct notcurses* n)
__attribute__ ((nonnull (1))) __attribute__ ((deprecated));
API int notcurses_mouse_disable(struct notcurses* n)
__attribute__ ((nonnull (1))) __attribute__ ((deprecated));
#undef API
#undef ALLOC

View File

@ -527,7 +527,7 @@ int main(int argc, char** argv){
if((nc = notcurses_init(&nopts, NULL)) == NULL){
return EXIT_FAILURE;
}
notcurses_mouse_enable(nc);
notcurses_mice_enable(nc, NCMICE_BUTTON_EVENT | NCMICE_DRAG_EVENT);
const bool canimage = notcurses_canopen_images(nc);
const bool canvideo = notcurses_canopen_videos(nc);
int dimx, dimy;

View File

@ -478,7 +478,7 @@ int main(int argc, const char** argv){
return EXIT_FAILURE;
}
// so that we know whether we're talking to gpm
notcurses_mouse_enable(nc);
notcurses_mice_enable(nc, NCMICE_ALL_EVENTS);
const char indent[] = "";
int dimx;
struct ncplane* stdn = notcurses_stddim_yx(nc, NULL, &dimx);

View File

@ -157,6 +157,7 @@ const char* nckeystr(char32_t spkey){
case NCKEY_RSUPER: return "right super";
case NCKEY_RHYPER: return "right hyper";
case NCKEY_RMETA: return "right meta";
case NCKEY_MOTION: return "mouse (no buttons pressed)";
case NCKEY_BUTTON1: return "mouse (button 1)";
case NCKEY_BUTTON2: return "mouse (button 2)";
case NCKEY_BUTTON3: return "mouse (button 3)";
@ -417,7 +418,7 @@ int main(int argc, char** argv){
}
nopts.flags = NCOPTION_INHIBIT_SETLOCALE;
NotCurses nc(nopts);
nc.mouse_enable(); // might fail if no mouse is available
nc.mouse_enable(NCMICE_ALL_EVENTS);
int ret = input_demo(&nc);
if(!nc.stop() || ret){
return EXIT_FAILURE;

View File

@ -515,13 +515,11 @@ mouse_click(inputctx* ictx, unsigned release, char follow){
.alt = mods & 0x08,
.shift = mods & 0x04,
};
if(mods < 64){
tni.id = NCKEY_BUTTON1 + (mods % 4);
}else if(mods >= 64 && mods < 128){
tni.id = NCKEY_BUTTON4 + (mods % 4);
}else if(mods >= 128 && mods < 192){
tni.id = NCKEY_BUTTON8 + (mods % 4);
}
// SGR mouse reporting: lower two bits signify base button + {0, 1, 2} press
// and no button pressed/release/{3}. bit 5 indicates motion. bits 6 and 7
// select device groups: 64 is buttons 4--7, 128 is 8--11. a pure motion
// report (no button) is 35 (32 + 3 (no button pressed)) with (oddly enough)
// 'M' (i.e. release == true).
if(release){
tni.evtype = NCTYPE_RELEASE;
}else{
@ -529,6 +527,18 @@ mouse_click(inputctx* ictx, unsigned release, char follow){
}
tni.x = x;
tni.y = y;
if(mods % 4 == 3){
tni.id = NCKEY_MOTION;
tni.evtype = NCTYPE_RELEASE;
}else{
if(mods < 64){
tni.id = NCKEY_BUTTON1 + (mods % 4);
}else if(mods >= 64 && mods < 128){
tni.id = NCKEY_BUTTON4 + (mods % 4);
}else if(mods >= 128 && mods < 192){
tni.id = NCKEY_BUTTON8 + (mods % 4);
}
}
load_ncinput(ictx, &tni, 0);
}
@ -1205,6 +1215,7 @@ xtversion_cb(inputctx* ictx){
return 2;
}
// XTGETTCAP responses are delimited by semicolons
static int
tcap_cb(inputctx* ictx){
char* str = amata_next_string(&ictx->amata, "\x1bP1+r");
@ -1216,25 +1227,32 @@ tcap_cb(inputctx* ictx){
free(str);
return 2;
}
// 'TN' (Terminal Name)
if(strncasecmp(str, "544e=", 5) == 0){
if(ictx->initdata->qterm != TERMINAL_UNKNOWN){
const char* tn = str + 5;
// FIXME clean this crap up
if(strcasecmp(tn, "6D6C7465726D") == 0){
ictx->initdata->qterm = TERMINAL_MLTERM;
}else if(strcasecmp(tn, "787465726d") == 0){
ictx->initdata->qterm = TERMINAL_XTERM; // "xterm"
}else if(strcasecmp(tn, "787465726d2d6b69747479") == 0){
ictx->initdata->qterm = TERMINAL_KITTY; // "xterm-kitty"
}else if(strcasecmp(tn, "787465726d2d323536636f6c6f72") == 0){
ictx->initdata->qterm = TERMINAL_XTERM; // "xterm-256color"
}else{
logdebug("unknown terminal name %s\n", tn);
const char* s = str;
while(*s){
// FIXME clean this crap up
if(strncasecmp(s, "544e=", 5) == 0){
if(ictx->initdata->qterm != TERMINAL_UNKNOWN){
const char* tn = s + 5;
if(strcasecmp(tn, "6D6C7465726D;") == 0){
ictx->initdata->qterm = TERMINAL_MLTERM;
}else if(strcasecmp(tn, "787465726d;") == 0){
ictx->initdata->qterm = TERMINAL_XTERM; // "xterm"
}else if(strcasecmp(tn, "787465726d2d6b69747479;") == 0){
ictx->initdata->qterm = TERMINAL_KITTY; // "xterm-kitty"
}else if(strcasecmp(tn, "787465726d2d323536636f6c6f72;") == 0){
ictx->initdata->qterm = TERMINAL_XTERM; // "xterm-256color"
}else{
logdebug("unknown terminal name %s\n", tn);
}
}
}else if(strncasecmp(s, "524742=", 7) == 0){
}else{
logdebug("unknown capability=val %s\n", str);
}
}else{
logdebug("unknown capability=val %s\n", str);
if((s = strchr(s, ';')) == NULL){
break;
}
++s;
}
free(str);
return 2;

View File

@ -1106,41 +1106,30 @@ coerce_styles(fbuf* f, const tinfo* ti, uint16_t* curstyle,
return ret;
}
#define SET_BTN_EVENT_MOUSE "1002"
#define SET_FOCUS_EVENT_MOUSE "1004"
#define SET_SGR_MODE_MOUSE "1006"
// DEC private mode set (DECSET) parameters (and corresponding XTerm resources)
#define SET_X10_MOUSE_PROT "9" // outdated, do not use, use x11
// we can combine 1000--1004, and then use 1005/1006/1015 for extended coordinates
#define SET_X11_MOUSE_PROT "1000" // for button events
#define SET_HILITE_MOUSE_PROT "1001" // for highlight tracking
#define SET_BTN_EVENT_MOUSE "1002" // for motion events with buttons
#define SET_ALL_EVENT_MOUSE "1003" // for motion events without buttons
#define SET_FOCUS_EVENT_MOUSE "1004" // for focus events
#define SET_UTF8_MOUSE_PROT "1005" // utf8-style extended coordinates
#define SET_SGR_MOUSE_PROT "1006" // sgr-style extended coordinates
#define SET_ALTERNATE_SCROLL "1007" // scroll in alternate screen (alternateScroll)
#define SET_TTYOUTPUT_SCROLL "1010" // scroll on tty output (scrollTtyOutput)
#define SET_KEYPRESS_SCROLL "1011" // scroll on keypress (scrollKey)
#define SET_URXVT_MOUSE_PROT "1015" // urxvt-style extended coordinates
#define SET_PIXEL_MOUSE_PROT "1016" // sgr-style, using pixles rather than cells
#define SET_ENABLE_ALTSCREEN "1046" // enable alternate screen (*sets* titeInhibit)
#define SET_ALTERNATE_SCREEN "1047" // replaces 47 (conflict w/DECGRPM) (titeInhibit)
#define SET_SAVE_CURSOR "1048" // save cursor ala DECSC (titeInhibit)
#define SET_SMCUP "1049" // 1047+1048 (titeInhibit)
// DECSET/DECRSTs can be chained with semicolons; can we generalize this? FIXME
#define DECSET(p) "\x1b[" p "h"
#define DECRST(p) "\x1b[" p "l"
static inline int
mouse_enable(tinfo* ti, FILE* out){
if(ti->qterm == TERMINAL_LINUX){
if(ti->gpmfd < 0){
if((ti->gpmfd = gpm_connect(ti)) < 0){
return -1;
}
}
return 0;
}
// Sets the shift-escape option, allowing shift+mouse to override the standard
// mouse protocol (mainly so copy-and-paste can still be performed).
#define XTSHIFTESCAPE "\x1b[>1s"
return term_emit(XTSHIFTESCAPE "\x1b[?" SET_BTN_EVENT_MOUSE ";"
/*SET_FOCUS_EVENT_MOUSE ";" */SET_SGR_MODE_MOUSE "h",
out, true);
#undef XTSHIFTESCAPE
}
static inline int
mouse_disable(tinfo* ti, fbuf* f){
if(ti->qterm == TERMINAL_LINUX){
if(ti->gpmfd < 0){
return 0;
}
ti->gpmfd = -1;
return gpm_close(ti);
}
return fbuf_emit(f, "\x1b[?" SET_BTN_EVENT_MOUSE ";"
/*SET_FOCUS_EVENT_MOUSE ";" */SET_SGR_MODE_MOUSE "l");
}
int mouse_setup(tinfo* ti, unsigned eventmask);
// sync the drawing position to the specified location with as little overhead
// as possible (with nothing, if already at the right location). we prefer

47
src/lib/mice.c Normal file
View File

@ -0,0 +1,47 @@
#include "internal.h"
int mouse_setup(tinfo* ti, unsigned eventmask){
if(ti->qterm == TERMINAL_LINUX){
if(eventmask == 0){
if(ti->gpmfd < 0){
return 0;
}
ti->gpmfd = -1;
return gpm_close(ti);
}
if(ti->gpmfd < 0){
// FIXME pass in eventmask
if((ti->gpmfd = gpm_connect(ti)) < 0){
return -1;
}
}
return 0;
}
if(ti->ttyfd < 0){
logerror("no tty, not emitting mouse control\n");
return -1;
}
// we'll need to fill in 'h' vs 'l' for both, and the event mode
char mode;
char command = 'h';
// we have to choose one event mode, where all > drag > button > none.
// if user wants *only* move and not button, we'll need filter those FIXME.
if(eventmask & NCMICE_MOVE_EVENT){
mode = '3'; // SET_ALL_EVENT_MOUSE
}else if(eventmask & NCMICE_DRAG_EVENT){
mode = '2'; // SET_BTN_EVENT_MOUSE
}else if(eventmask & NCMICE_BUTTON_EVENT){
mode = '0'; // SET_X11_MOUSE_PROT
}else{
mode = '3';
command = 'l';
}
// Sets the shift-escape option, allowing shift+mouse to override the standard
// mouse protocol (mainly so copy-and-paste can still be performed).
#define XTSHIFTESCAPE "\x1b[>1s"
char mousecmd[] = XTSHIFTESCAPE "\x1b[?100x;" SET_SGR_MOUSE_PROT "x";
mousecmd[11] = mode;
mousecmd[17] = command;
return tty_emit(mousecmd, ti->ttyfd);
#undef XTSHIFTESCAPE
}

View File

@ -80,7 +80,6 @@ notcurses_stop_minimal(void* vnc){
// be sure to write the restoration sequences *prior* to running rmcup, as
// they apply to the screen (alternate or otherwise) we're actually using.
const char* esc;
ret |= mouse_disable(&nc->tcache, f);
if((esc = get_escape(&nc->tcache, ESCAPE_RESTORECOLORS)) && fbuf_emit(f, esc)){
ret = -1;
}
@ -96,6 +95,7 @@ notcurses_stop_minimal(void* vnc){
ret = -1;
}
fbuf_reset(f);
ret |= mouse_setup(&nc->tcache, NCMICE_NO_EVENTS);
if(nc->tcache.ttyfd >= 0){
if(nc->tcache.tpreserved){
ret |= tcsetattr(nc->tcache.ttyfd, TCSAFLUSH, nc->tcache.tpreserved);
@ -2285,30 +2285,26 @@ ncplane* ncplane_above(ncplane* n){
return n->above;
}
int notcurses_mouse_enable(notcurses* n){
if(mouse_enable(&n->tcache, n->ttyfp)){
int notcurses_mice_enable(notcurses* n, unsigned eventmask){
if(mouse_setup(&n->tcache, eventmask)){
return -1;
}
return 0;
}
// this seems to work (note difference in suffix, 'l' vs 'h'), but what about
// the sequences 1000 etc?
int notcurses_mouse_disable(notcurses* n){
fbuf f = {};
if(fbuf_init_small(&f)){
return -1;
}
if(mouse_disable(&n->tcache, &f)){
fbuf_free(&f);
return -1;
}
if(fbuf_finalize(&f, n->ttyfp) < 0){
// FIXME begone in abi3
int notcurses_mouse_enable(notcurses* n){
if(notcurses_mice_enable(n, NCMICE_BUTTON_EVENT)){
return -1;
}
return 0;
}
// FIXME begone in abi3
int notcurses_mouse_disable(notcurses* n){
return notcurses_mice_disable(n);
}
bool notcurses_canutf8(const notcurses* nc){
return nc->tcache.caps.utf8;
}

View File

@ -1540,9 +1540,7 @@ int notcurses_render(notcurses* nc){
return i;
}
// for now, we just run the top half of notcurses_render(), and copy out the
// memstream from within rstate. we want to allocate our own here, and return
// it, to avoid the copy, but we need feed the params through to do so FIXME.
// run the top half of notcurses_render(), and steal the buffer from rstate.
int ncpile_render_to_buffer(ncplane* p, char** buf, size_t* buflen){
if(ncpile_render(p)){
return -1;
@ -1557,11 +1555,9 @@ int ncpile_render_to_buffer(ncplane* p, char** buf, size_t* buflen){
if(bytes < 0){
return -1;
}
*buf = memdup(nc->rstate.f.buf, nc->rstate.f.used);
if(buf == NULL){
return -1;
}
*buf = nc->rstate.f.buf;
*buflen = nc->rstate.f.used;
fbuf_reset(&nc->rstate.f);
return 0;
}

View File

@ -308,8 +308,8 @@ init_terminfo_esc(tinfo* ti, const char* name, escape_e idx,
// XTVERSION. Replies with DCS > | ... ST
#define XTVERSION "\x1b[>0q"
// XTGETTCAP['TN'] (Terminal Name)
#define XTGETTCAPTN "\x1bP+q544e\x1b\\"
// XTGETTCAP['TN', 'RGB'] (Terminal Name, RGB)
#define XTGETTCAP "\x1bP+q544e;524742\x1b\\"
// Secondary Device Attributes, necessary to get Alacritty's version. Since
// this doesn't uniquely identify a terminal, we ask it last, so that if any
@ -346,14 +346,14 @@ init_terminfo_esc(tinfo* ti, const char* name, escape_e idx,
// to, there's no point in sending them.
#define IDQUERIES TRIDEVATTR \
XTVERSION \
XTGETTCAPTN \
XTGETTCAP \
SECDEVATTR
// query background, replies in X color https://www.x.org/releases/X11R7.7/doc/man/man7/X.7.xhtml#heading11
// GNU screen passes this on to the underlying terminal rather than answering
// itself, unlike most other queries, so send this first since it will take
// longer to be answered.
#define CSI_BGQ "\x1b]11;?\e\\"
// GNU screen passes this on to the underlying terminal rather than answering itself,
// unlike most other queries, so send this first since it will take longer to be
// answered. note the "\x1b]"; this is an Operating System Command, not CSI.
#define DEFBGQ "\x1b]11;?\e\\"
// FIXME ought be using the u7 terminfo string here, if it exists. the great
// thing is, if we get a response to this, we know we can use it for u7!
@ -379,7 +379,7 @@ init_terminfo_esc(tinfo* ti, const char* name, escape_e idx,
// request the cell geometry of the textual area
#define GEOMCELL "\x1b[18t"
#define DIRECTIVES CSI_BGQ \
#define DIRECTIVES DEFBGQ \
KKBDQUERY \
SUMQUERY \
"\x1b[?1;3;256S" /* try to set 256 cregs */ \
@ -400,8 +400,8 @@ init_terminfo_esc(tinfo* ti, const char* name, escape_e idx,
// enter the alternate screen (smcup). we could technically get this from
// terminfo, but everyone who supports it supports it the same way, and we
// need to send it before our other directives if we're going to use it.
#define SMCUP "\x1b[?1049h"
#define RMCUP "\x1b[?1049l"
#define SMCUP DECSET(SET_SMCUP)
#define RMCUP DECRST(SET_SMCUP)
// we send an XTSMGRAPHICS to set up 256 color registers (the most we can
// currently take advantage of; we need at least 64 to use sixel at all).

View File

@ -62,17 +62,12 @@ err:
}
int main(void){
if(!setlocale(LC_ALL, "")){
return EXIT_FAILURE;
}
notcurses_options opts = {
.flags = NCOPTION_INHIBIT_SETLOCALE,
};
notcurses_options opts = { };
struct notcurses* nc = notcurses_core_init(&opts, NULL);
if(nc == NULL){
return EXIT_FAILURE;
}
notcurses_mouse_enable(nc);
notcurses_mice_enable(nc, NCMICE_BUTTON_EVENT);
struct ncmenu_item demo_items[] = {
{ .desc = "Restart", .shortcut = { .id = 'r', .ctrl = true, }, },
{ .desc = "Disabled", .shortcut = { .id = 'd', .ctrl = false, }, },

View File

@ -62,18 +62,14 @@ run_mselect(struct notcurses* nc, struct ncmultiselector* ns){
}
int main(void){
if(!setlocale(LC_ALL, "")){
return EXIT_FAILURE;
}
notcurses_options opts = {
.flags = NCOPTION_INHIBIT_SETLOCALE,
.loglevel = NCLOGLEVEL_ERROR,
};
struct notcurses* nc = notcurses_init(&opts, NULL);
if(nc == NULL){
return EXIT_FAILURE;
}
notcurses_mouse_enable(nc);
notcurses_mice_enable(nc, NCMICE_BUTTON_EVENT);
ncmultiselector_options sopts;
memset(&sopts, 0, sizeof(sopts));
sopts.maxdisplay = 10;

View File

@ -52,17 +52,12 @@ run_selector(struct notcurses* nc, struct ncselector* ns){
}
int main(void){
if(!setlocale(LC_ALL, "")){
return EXIT_FAILURE;
}
notcurses_options opts = {
.flags = NCOPTION_INHIBIT_SETLOCALE,
};
notcurses_options opts = { };
struct notcurses* nc = notcurses_init(&opts, NULL);
if(nc == NULL){
return EXIT_FAILURE;
}
notcurses_mouse_enable(nc);
notcurses_mice_enable(nc, NCMICE_BUTTON_EVENT);
ncselector_options sopts;
memset(&sopts, 0, sizeof(sopts));
sopts.maxdisplay = 4;