mirror of
https://github.com/neovim/neovim.git
synced 2025-10-18 07:41:51 +00:00
Merge #7048 from justinmk/log-ui
This commit is contained in:
@@ -1,14 +1,19 @@
|
|||||||
## Source code overview
|
|
||||||
|
|
||||||
This document is an overview of how Nvim works internally, focusing on parts
|
Module-specific details are documented at the top of each module (e.g.
|
||||||
that are different from Vim. Since Nvim inherited from Vim, some information in
|
terminal.c, screen.c).
|
||||||
[its README](https://raw.githubusercontent.com/vim/vim/master/src/README.txt)
|
|
||||||
still applies.
|
|
||||||
|
|
||||||
For module-specific details, read the source code. Some files are extensively
|
See also `:help development`.
|
||||||
commented at the top (e.g. terminal.c, screen.c).
|
|
||||||
|
|
||||||
### Source file name conventions
|
UI Debugging
|
||||||
|
------------
|
||||||
|
|
||||||
|
At `DEBUG_LOG_LEVEL`, all UI events are logged.
|
||||||
|
|
||||||
|
rm -rf build/
|
||||||
|
make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0"
|
||||||
|
|
||||||
|
Filename conventions
|
||||||
|
--------------------
|
||||||
|
|
||||||
The source files use extensions to hint about their purpose.
|
The source files use extensions to hint about their purpose.
|
||||||
|
|
||||||
@@ -19,10 +24,12 @@ The source files use extensions to hint about their purpose.
|
|||||||
- `*.h.generated.h` - exported functions’ declarations.
|
- `*.h.generated.h` - exported functions’ declarations.
|
||||||
- `*.c.generated.h` - static functions’ declarations.
|
- `*.c.generated.h` - static functions’ declarations.
|
||||||
|
|
||||||
### Top-level program loops
|
Nvim lifecycle
|
||||||
|
--------------
|
||||||
|
|
||||||
Let's understand what a Vim-like program does by analyzing the workflow of
|
Following describes how Nvim processes input.
|
||||||
a typical editing session:
|
|
||||||
|
Consider a typical Vim-like editing session:
|
||||||
|
|
||||||
01. Vim dispays the welcome screen
|
01. Vim dispays the welcome screen
|
||||||
02. User types: `:`
|
02. User types: `:`
|
||||||
@@ -154,7 +161,8 @@ modes managed by the `state_enter` loop:
|
|||||||
- insert mode: `insert_{enter,check,execute}()`(`edit.c`)
|
- insert mode: `insert_{enter,check,execute}()`(`edit.c`)
|
||||||
- terminal mode: `terminal_{enter,execute}()`(`terminal.c`)
|
- terminal mode: `terminal_{enter,execute}()`(`terminal.c`)
|
||||||
|
|
||||||
### Async event support
|
Async event support
|
||||||
|
-------------------
|
||||||
|
|
||||||
One of the features Nvim added is the support for handling arbitrary
|
One of the features Nvim added is the support for handling arbitrary
|
||||||
asynchronous events, which can include:
|
asynchronous events, which can include:
|
||||||
|
@@ -15141,7 +15141,8 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char *error = NULL;
|
const char *error = NULL;
|
||||||
uint64_t id = channel_connect(tcp, address, 50, &error);
|
eval_format_source_name_line((char *)IObuff, sizeof(IObuff));
|
||||||
|
uint64_t id = channel_connect(tcp, address, 50, (char *)IObuff, &error);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
EMSG2(_("connection failed: %s"), error);
|
EMSG2(_("connection failed: %s"), error);
|
||||||
@@ -22449,8 +22450,9 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
|
|||||||
|
|
||||||
|
|
||||||
if (data->rpc) {
|
if (data->rpc) {
|
||||||
// the rpc channel takes over the in and out streams
|
eval_format_source_name_line((char *)IObuff, sizeof(IObuff));
|
||||||
channel_from_process(proc, data->id);
|
// RPC channel takes over the in/out streams.
|
||||||
|
channel_from_process(proc, data->id, (char *)IObuff);
|
||||||
} else {
|
} else {
|
||||||
wstream_init(proc->in, 0);
|
wstream_init(proc->in, 0);
|
||||||
if (proc->out) {
|
if (proc->out) {
|
||||||
@@ -22775,3 +22777,11 @@ bool eval_has_provider(const char *name)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
|
||||||
|
void eval_format_source_name_line(char *buf, size_t bufsize)
|
||||||
|
{
|
||||||
|
snprintf(buf, bufsize, "%s:%" PRIdLINENR,
|
||||||
|
(sourcing_name ? sourcing_name : (char_u *)"?"),
|
||||||
|
(sourcing_name ? sourcing_lnum : 0));
|
||||||
|
}
|
||||||
|
@@ -119,7 +119,7 @@ for i = 1, #events do
|
|||||||
write_signature(bridge_output, ev, 'UI *ui')
|
write_signature(bridge_output, ev, 'UI *ui')
|
||||||
bridge_output:write('\n{\n')
|
bridge_output:write('\n{\n')
|
||||||
bridge_output:write(send)
|
bridge_output:write(send)
|
||||||
bridge_output:write(' UI_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n')
|
bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -128,6 +128,7 @@ for i = 1, #events do
|
|||||||
call_output:write('\n{\n')
|
call_output:write('\n{\n')
|
||||||
if ev.remote_only then
|
if ev.remote_only then
|
||||||
write_arglist(call_output, ev, false)
|
write_arglist(call_output, ev, false)
|
||||||
|
call_output:write(' UI_LOG('..ev.name..', 0);\n')
|
||||||
call_output:write(' ui_event("'..ev.name..'", args);\n')
|
call_output:write(' ui_event("'..ev.name..'", args);\n')
|
||||||
else
|
else
|
||||||
call_output:write(' UI_CALL')
|
call_output:write(' UI_CALL')
|
||||||
|
@@ -117,12 +117,15 @@ void channel_teardown(void)
|
|||||||
/// Creates an API channel by starting a process and connecting to its
|
/// Creates an API channel by starting a process and connecting to its
|
||||||
/// stdin/stdout. stderr is handled by the job infrastructure.
|
/// stdin/stdout. stderr is handled by the job infrastructure.
|
||||||
///
|
///
|
||||||
/// @param argv The argument vector for the process. [consumed]
|
/// @param proc process object
|
||||||
/// @return The channel id (> 0), on success.
|
/// @param id (optional) channel id
|
||||||
/// 0, on error.
|
/// @param source description of source function, rplugin name, TCP addr, etc
|
||||||
uint64_t channel_from_process(Process *proc, uint64_t id)
|
///
|
||||||
|
/// @return Channel id (> 0), on success. 0, on error.
|
||||||
|
uint64_t channel_from_process(Process *proc, uint64_t id, char *source)
|
||||||
{
|
{
|
||||||
Channel *channel = register_channel(kChannelTypeProc, id, proc->events);
|
Channel *channel = register_channel(kChannelTypeProc, id, proc->events,
|
||||||
|
source);
|
||||||
incref(channel); // process channels are only closed by the exit_cb
|
incref(channel); // process channels are only closed by the exit_cb
|
||||||
channel->data.proc = proc;
|
channel->data.proc = proc;
|
||||||
|
|
||||||
@@ -138,7 +141,8 @@ uint64_t channel_from_process(Process *proc, uint64_t id)
|
|||||||
/// @param watcher The SocketWatcher ready to accept the connection
|
/// @param watcher The SocketWatcher ready to accept the connection
|
||||||
void channel_from_connection(SocketWatcher *watcher)
|
void channel_from_connection(SocketWatcher *watcher)
|
||||||
{
|
{
|
||||||
Channel *channel = register_channel(kChannelTypeSocket, 0, NULL);
|
Channel *channel = register_channel(kChannelTypeSocket, 0, NULL,
|
||||||
|
watcher->addr);
|
||||||
socket_watcher_accept(watcher, &channel->data.stream);
|
socket_watcher_accept(watcher, &channel->data.stream);
|
||||||
incref(channel); // close channel only after the stream is closed
|
incref(channel); // close channel only after the stream is closed
|
||||||
channel->data.stream.internal_close_cb = close_cb;
|
channel->data.stream.internal_close_cb = close_cb;
|
||||||
@@ -148,8 +152,9 @@ void channel_from_connection(SocketWatcher *watcher)
|
|||||||
rstream_start(&channel->data.stream, receive_msgpack, channel);
|
rstream_start(&channel->data.stream, receive_msgpack, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t channel_connect(bool tcp, const char *address,
|
/// @param source description of source function, rplugin name, TCP addr, etc
|
||||||
int timeout, const char **error)
|
uint64_t channel_connect(bool tcp, const char *address, int timeout,
|
||||||
|
char *source, const char **error)
|
||||||
{
|
{
|
||||||
if (!tcp) {
|
if (!tcp) {
|
||||||
char *path = fix_fname(address);
|
char *path = fix_fname(address);
|
||||||
@@ -161,7 +166,7 @@ uint64_t channel_connect(bool tcp, const char *address,
|
|||||||
xfree(path);
|
xfree(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel *channel = register_channel(kChannelTypeSocket, 0, NULL);
|
Channel *channel = register_channel(kChannelTypeSocket, 0, NULL, source);
|
||||||
if (!socket_connect(&main_loop, &channel->data.stream,
|
if (!socket_connect(&main_loop, &channel->data.stream,
|
||||||
tcp, address, timeout, error)) {
|
tcp, address, timeout, error)) {
|
||||||
decref(channel);
|
decref(channel);
|
||||||
@@ -329,11 +334,10 @@ bool channel_close(uint64_t id)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an API channel from stdin/stdout. This is used when embedding
|
/// Creates an API channel from stdin/stdout. Used to embed Nvim.
|
||||||
/// Neovim
|
|
||||||
void channel_from_stdio(void)
|
void channel_from_stdio(void)
|
||||||
{
|
{
|
||||||
Channel *channel = register_channel(kChannelTypeStdio, 0, NULL);
|
Channel *channel = register_channel(kChannelTypeStdio, 0, NULL, NULL);
|
||||||
incref(channel); // stdio channels are only closed on exit
|
incref(channel); // stdio channels are only closed on exit
|
||||||
// read stream
|
// read stream
|
||||||
rstream_init_fd(&main_loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE);
|
rstream_init_fd(&main_loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE);
|
||||||
@@ -346,7 +350,7 @@ void channel_from_stdio(void)
|
|||||||
/// when an instance connects to its own named pipe.
|
/// when an instance connects to its own named pipe.
|
||||||
uint64_t channel_create_internal(void)
|
uint64_t channel_create_internal(void)
|
||||||
{
|
{
|
||||||
Channel *channel = register_channel(kChannelTypeInternal, 0, NULL);
|
Channel *channel = register_channel(kChannelTypeInternal, 0, NULL, NULL);
|
||||||
incref(channel); // internal channel lives until process exit
|
incref(channel); // internal channel lives until process exit
|
||||||
return channel->id;
|
return channel->id;
|
||||||
}
|
}
|
||||||
@@ -745,9 +749,12 @@ static void close_cb(Stream *stream, void *data)
|
|||||||
decref(data);
|
decref(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @param source description of source function, rplugin name, TCP addr, etc
|
||||||
static Channel *register_channel(ChannelType type, uint64_t id,
|
static Channel *register_channel(ChannelType type, uint64_t id,
|
||||||
MultiQueue *events)
|
MultiQueue *events, char *source)
|
||||||
{
|
{
|
||||||
|
// Jobs and channels share the same id namespace.
|
||||||
|
assert(id == 0 || !pmap_get(uint64_t)(channels, id));
|
||||||
Channel *rv = xmalloc(sizeof(Channel));
|
Channel *rv = xmalloc(sizeof(Channel));
|
||||||
rv->events = events ? events : multiqueue_new_child(main_loop.events);
|
rv->events = events ? events : multiqueue_new_child(main_loop.events);
|
||||||
rv->type = type;
|
rv->type = type;
|
||||||
@@ -761,6 +768,14 @@ static Channel *register_channel(ChannelType type, uint64_t id,
|
|||||||
kv_init(rv->call_stack);
|
kv_init(rv->call_stack);
|
||||||
kv_init(rv->delayed_notifications);
|
kv_init(rv->delayed_notifications);
|
||||||
pmap_put(uint64_t)(channels, rv->id, rv);
|
pmap_put(uint64_t)(channels, rv->id, rv);
|
||||||
|
|
||||||
|
ILOG("new channel %" PRIu64 " (%s): %s", rv->id,
|
||||||
|
(type == kChannelTypeProc ? "proc"
|
||||||
|
: (type == kChannelTypeSocket ? "socket"
|
||||||
|
: (type == kChannelTypeStdio ? "stdio"
|
||||||
|
: (type == kChannelTypeInternal ? "internal" : "?")))),
|
||||||
|
(source ? source : "?"));
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1007,7 +1007,7 @@ static void tui_flush(UI *ui)
|
|||||||
|
|
||||||
size_t nrevents = loop_size(data->loop);
|
size_t nrevents = loop_size(data->loop);
|
||||||
if (nrevents > TOO_MANY_EVENTS) {
|
if (nrevents > TOO_MANY_EVENTS) {
|
||||||
ILOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents);
|
WLOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents);
|
||||||
// Back-pressure: UI events may accumulate much faster than the terminal
|
// Back-pressure: UI events may accumulate much faster than the terminal
|
||||||
// device can serve them. Even if SIGINT/CTRL-C is received, user must still
|
// device can serve them. Even if SIGINT/CTRL-C is received, user must still
|
||||||
// wait for the TUI event-queue to drain, and if there are ~millions of
|
// wait for the TUI event-queue to drain, and if there are ~millions of
|
||||||
@@ -1690,7 +1690,7 @@ static const char *tui_get_stty_erase(void)
|
|||||||
if (tcgetattr(input_global_fd(), &t) != -1) {
|
if (tcgetattr(input_global_fd(), &t) != -1) {
|
||||||
stty_erase[0] = (char)t.c_cc[VERASE];
|
stty_erase[0] = (char)t.c_cc[VERASE];
|
||||||
stty_erase[1] = '\0';
|
stty_erase[1] = '\0';
|
||||||
ILOG("stty/termios:erase=%s", stty_erase);
|
DLOG("stty/termios:erase=%s", stty_erase);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return stty_erase;
|
return stty_erase;
|
||||||
@@ -1707,12 +1707,12 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (strequal(name, "key_backspace")) {
|
if (strequal(name, "key_backspace")) {
|
||||||
ILOG("libtermkey:kbs=%s", value);
|
DLOG("libtermkey:kbs=%s", value);
|
||||||
if (stty_erase[0] != 0) {
|
if (stty_erase[0] != 0) {
|
||||||
return stty_erase;
|
return stty_erase;
|
||||||
}
|
}
|
||||||
} else if (strequal(name, "key_dc")) {
|
} else if (strequal(name, "key_dc")) {
|
||||||
ILOG("libtermkey:kdch1=%s", value);
|
DLOG("libtermkey:kdch1=%s", value);
|
||||||
// Vim: "If <BS> and <DEL> are now the same, redefine <DEL>."
|
// Vim: "If <BS> and <DEL> are now the same, redefine <DEL>."
|
||||||
if (value != NULL && strequal(stty_erase, value)) {
|
if (value != NULL && strequal(stty_erase, value)) {
|
||||||
return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR;
|
return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR;
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
|
#include "nvim/log.h"
|
||||||
#include "nvim/ui.h"
|
#include "nvim/ui.h"
|
||||||
#include "nvim/charset.h"
|
#include "nvim/charset.h"
|
||||||
#include "nvim/cursor.h"
|
#include "nvim/cursor.h"
|
||||||
@@ -59,6 +60,27 @@ static int busy = 0;
|
|||||||
static int height, width;
|
static int height, width;
|
||||||
static int old_mode_idx = -1;
|
static int old_mode_idx = -1;
|
||||||
|
|
||||||
|
#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
|
||||||
|
# define UI_LOG(funname, ...)
|
||||||
|
#else
|
||||||
|
static size_t uilog_seen = 0;
|
||||||
|
static char uilog_last_event[1024] = { 0 };
|
||||||
|
# define UI_LOG(funname, ...) \
|
||||||
|
do { \
|
||||||
|
if (strequal(uilog_last_event, STR(funname))) { \
|
||||||
|
uilog_seen++; \
|
||||||
|
} else { \
|
||||||
|
if (uilog_seen > 0) { \
|
||||||
|
do_log(DEBUG_LOG_LEVEL, "ui", 0, true, \
|
||||||
|
"%s (+%zu times...)", uilog_last_event, uilog_seen); \
|
||||||
|
} \
|
||||||
|
DLOG("ui: " STR(funname)); \
|
||||||
|
uilog_seen = 0; \
|
||||||
|
xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
// UI_CALL invokes a function on all registered UI instances. The functions can
|
// UI_CALL invokes a function on all registered UI instances. The functions can
|
||||||
// have 0-5 arguments (configurable by SELECT_NTH).
|
// have 0-5 arguments (configurable by SELECT_NTH).
|
||||||
//
|
//
|
||||||
@@ -67,6 +89,7 @@ static int old_mode_idx = -1;
|
|||||||
# define UI_CALL(funname, ...) \
|
# define UI_CALL(funname, ...) \
|
||||||
do { \
|
do { \
|
||||||
flush_cursor_update(); \
|
flush_cursor_update(); \
|
||||||
|
UI_LOG(funname, 0); \
|
||||||
for (size_t i = 0; i < ui_count; i++) { \
|
for (size_t i = 0; i < ui_count; i++) { \
|
||||||
UI *ui = uis[i]; \
|
UI *ui = uis[i]; \
|
||||||
UI_CALL_MORE(funname, __VA_ARGS__); \
|
UI_CALL_MORE(funname, __VA_ARGS__); \
|
||||||
@@ -76,6 +99,7 @@ static int old_mode_idx = -1;
|
|||||||
# define UI_CALL(...) \
|
# define UI_CALL(...) \
|
||||||
do { \
|
do { \
|
||||||
flush_cursor_update(); \
|
flush_cursor_update(); \
|
||||||
|
UI_LOG(__VA_ARGS__, 0); \
|
||||||
for (size_t i = 0; i < ui_count; i++) { \
|
for (size_t i = 0; i < ui_count; i++) { \
|
||||||
UI *ui = uis[i]; \
|
UI *ui = uis[i]; \
|
||||||
UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
|
UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
|
||||||
@@ -85,6 +109,7 @@ static int old_mode_idx = -1;
|
|||||||
#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore)
|
#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore)
|
||||||
#define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6
|
#define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6
|
||||||
#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
|
#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
|
||||||
|
// Resolves to UI_CALL_MORE or UI_CALL_ZERO.
|
||||||
#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
|
#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
|
||||||
#define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__)
|
#define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__)
|
||||||
#define UI_CALL_ZERO(method) if (ui->method) ui->method(ui)
|
#define UI_CALL_ZERO(method) if (ui->method) ui->method(ui)
|
||||||
|
@@ -24,30 +24,10 @@
|
|||||||
|
|
||||||
#define UI(b) (((UIBridgeData *)b)->ui)
|
#define UI(b) (((UIBridgeData *)b)->ui)
|
||||||
|
|
||||||
#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
|
|
||||||
static size_t uilog_seen = 0;
|
|
||||||
static argv_callback uilog_event = NULL;
|
|
||||||
#define UI_CALL(ui, name, argc, ...) \
|
|
||||||
do { \
|
|
||||||
if (uilog_event == ui_bridge_##name##_event) { \
|
|
||||||
uilog_seen++; \
|
|
||||||
} else { \
|
|
||||||
if (uilog_seen > 0) { \
|
|
||||||
DLOG("UI bridge: ...%zu times", uilog_seen); \
|
|
||||||
} \
|
|
||||||
DLOG("UI bridge: " STR(name)); \
|
|
||||||
uilog_seen = 0; \
|
|
||||||
uilog_event = ui_bridge_##name##_event; \
|
|
||||||
} \
|
|
||||||
((UIBridgeData *)ui)->scheduler( \
|
|
||||||
event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)); \
|
|
||||||
} while (0)
|
|
||||||
#else
|
|
||||||
// Schedule a function call on the UI bridge thread.
|
// Schedule a function call on the UI bridge thread.
|
||||||
#define UI_CALL(ui, name, argc, ...) \
|
#define UI_BRIDGE_CALL(ui, name, argc, ...) \
|
||||||
((UIBridgeData *)ui)->scheduler( \
|
((UIBridgeData *)ui)->scheduler( \
|
||||||
event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui))
|
event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui))
|
||||||
#endif
|
|
||||||
|
|
||||||
#define INT2PTR(i) ((void *)(intptr_t)i)
|
#define INT2PTR(i) ((void *)(intptr_t)i)
|
||||||
#define PTR2INT(p) ((Integer)(intptr_t)p)
|
#define PTR2INT(p) ((Integer)(intptr_t)p)
|
||||||
@@ -128,7 +108,7 @@ static void ui_bridge_stop(UI *b)
|
|||||||
{
|
{
|
||||||
UIBridgeData *bridge = (UIBridgeData *)b;
|
UIBridgeData *bridge = (UIBridgeData *)b;
|
||||||
bool stopped = bridge->stopped = false;
|
bool stopped = bridge->stopped = false;
|
||||||
UI_CALL(b, stop, 1, b);
|
UI_BRIDGE_CALL(b, stop, 1, b);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
uv_mutex_lock(&bridge->mutex);
|
uv_mutex_lock(&bridge->mutex);
|
||||||
stopped = bridge->stopped;
|
stopped = bridge->stopped;
|
||||||
@@ -154,7 +134,7 @@ static void ui_bridge_highlight_set(UI *b, HlAttrs attrs)
|
|||||||
{
|
{
|
||||||
HlAttrs *a = xmalloc(sizeof(HlAttrs));
|
HlAttrs *a = xmalloc(sizeof(HlAttrs));
|
||||||
*a = attrs;
|
*a = attrs;
|
||||||
UI_CALL(b, highlight_set, 2, b, a);
|
UI_BRIDGE_CALL(b, highlight_set, 2, b, a);
|
||||||
}
|
}
|
||||||
static void ui_bridge_highlight_set_event(void **argv)
|
static void ui_bridge_highlight_set_event(void **argv)
|
||||||
{
|
{
|
||||||
@@ -167,7 +147,7 @@ static void ui_bridge_suspend(UI *b)
|
|||||||
{
|
{
|
||||||
UIBridgeData *data = (UIBridgeData *)b;
|
UIBridgeData *data = (UIBridgeData *)b;
|
||||||
uv_mutex_lock(&data->mutex);
|
uv_mutex_lock(&data->mutex);
|
||||||
UI_CALL(b, suspend, 1, b);
|
UI_BRIDGE_CALL(b, suspend, 1, b);
|
||||||
data->ready = false;
|
data->ready = false;
|
||||||
// suspend the main thread until CONTINUE is called by the UI thread
|
// suspend the main thread until CONTINUE is called by the UI thread
|
||||||
while (!data->ready) {
|
while (!data->ready) {
|
||||||
|
Reference in New Issue
Block a user