mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 09:09:03 -04:00
[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:
parent
960414d818
commit
0f5aec510a
9
NEWS.md
9
NEWS.md
@ -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
|
||||
|
@ -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)**,
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
66
src/lib/in.c
66
src/lib/in.c
@ -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;
|
||||
|
@ -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
47
src/lib/mice.c
Normal 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
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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).
|
||||
|
@ -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, }, },
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user