Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
de099f1
feat: add webview_output module with QOI rendering
yzr278892 Apr 28, 2026
bf373d9
feat: add 7 new editor commands for webview preview
yzr278892 Apr 28, 2026
7946813
feat: add -webview mode to driver and main event loop
yzr278892 Apr 28, 2026
0fc3d86
fix: build fixes for webview mode - PATH_MAX include, QOI linking
yzr278892 Apr 28, 2026
7cfb884
fix(tikz): prevent page rendering abort on unrecognized specials
yzr278892 Apr 28, 2026
39d4efa
feat(tikz): add pdf:literal handler, route to pdf_code parser
yzr278892 Apr 28, 2026
376ef5c
fix: webview mode crash and TikZ PDF v/y curve operators
yzr278892 Apr 28, 2026
3a1eba7
fix: use SDL_WINDOW_HIDDEN then ShowWindow to avoid blocking
yzr278892 Apr 28, 2026
bf569bc
fix: render pages immediately in webview mode, fix mkstemp template
yzr278892 Apr 28, 2026
dd5be7c
feat: dark mode support, incremental render fix, light mode default
yzr278892 Apr 28, 2026
008a9a9
fix: zoom system, scroll, SyncTeX, preview lifecycle, page dimensions…
yzr278892 Apr 29, 2026
c66a912
fix: incremental rendering with dirty rects, SyncTeX coords, time-bas…
yzr278892 Apr 29, 2026
41e3ac4
fix: real-time rendering, page preloading, page input error handling
yzr278892 Apr 29, 2026
e6e375d
fix: optimize real-time rendering, remove redundant render_page
yzr278892 Apr 29, 2026
b6a4c66
fix: remove double webview render and lower default resolution to 2x
yzr278892 Apr 29, 2026
39e9f4b
fix: restore immediate webview render with deduplication guard
yzr278892 Apr 29, 2026
b8b2038
feat: auto-preload all pages in webview mode, fix go-end cascading
yzr278892 Apr 29, 2026
d29eb15
fix: prevent busy loop in webview preloading by only continuing on ne…
yzr278892 Apr 29, 2026
987f6b4
fix: revert complex event-loop changes, use simple preload phase
yzr278892 Apr 29, 2026
0d76569
fix: poll engine continuously instead of breaking on step() false
yzr278892 Apr 29, 2026
54eb2c1
refactor: remove sync preload, use on-demand page discovery
yzr278892 Apr 29, 2026
ed49434
fix: reduce go-end/set-page stalled limit to 50000 (~500ms)
yzr278892 Apr 29, 2026
652d303
feat: send synctex-scroll position to webview preview
yzr278892 Apr 29, 2026
29fbe8b
fix: change C auto-detect resolution from 2x to 2.5x to match webview…
yzr278892 Apr 29, 2026
0202d79
feat: add -resolution flag to control C-side auto-detect resolution
yzr278892 Apr 29, 2026
2754af2
feat: add screenshots and PR summary
yzr278892 Apr 29, 2026
71ebebf
fix: address Copilot review comments for webview PR
yzr278892 Apr 30, 2026
5c99bcc
fix: remove TikZ changes from builtin-preview branch
yzr278892 Apr 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added built-in-preview-window-option.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added built-in-preview-window.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/frontend/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
OBJECTS=sprotocol.o state.o fs.o incdvi.o myabort.o renderer.o engine_tex.o engine_pdf.o engine_dvi.o synctex.o prot_parser.o sexp_parser.o json_parser.o editor.o
OBJECTS=sprotocol.o state.o fs.o incdvi.o myabort.o renderer.o engine_tex.o engine_pdf.o engine_dvi.o synctex.o prot_parser.o sexp_parser.o json_parser.o editor.o webview_output.o

BUILD=../../build
DIR=$(BUILD)/frontend
Expand Down
115 changes: 87 additions & 28 deletions src/frontend/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static void usage(void)
{
fprintf(stderr,
"Usage: texpresso [-I path]* [-json] [-lines] [-texlive] [-tectonic] "
"[-test-initialize] [-stream] root_file.tex\n");
"[-test-initialize] [-stream] [-webview] [-tmpdir path] [-resolution N] root_file.tex\n");
fprintf(stderr,
" -I path Add a path to included directories. \n"
" Files are looked up relative to document directory and all "
Expand All @@ -125,6 +125,12 @@ static void usage(void)
" -test-initialize Run a single cycle for test purposes\n");
fprintf(stderr,
" -stream Skip filesystem lookups; files are pushed via editor commands\n");
fprintf(stderr,
" -webview Run in webview mode (output QOI files via stdout, no SDL window)\n");
fprintf(stderr,
" -tmpdir Set temporary directory for QOI output files\n");
fprintf(stderr,
" -resolution N Default rendering resolution multiplier (default 2.5)\n");
}

int main(int argc, const char **argv)
Expand Down Expand Up @@ -155,6 +161,9 @@ int main(int argc, const char **argv)
bool use_texlive = 0;
bool initialize_only = 0;
bool stream_mode = 0;
bool webview_mode = 0;
float default_resolution = 2.5f;
char tmpdir[4096] = {0};

int inclusion_path_size = 1;
for (int i = 1; i < argc; i++)
Expand Down Expand Up @@ -198,6 +207,33 @@ int main(int argc, const char **argv)
{
stream_mode = 1;
}
else if (strcmp(arg, "-webview") == 0)
{
webview_mode = 1;
}
else if (strcmp(arg, "-tmpdir") == 0)
{
i += 1;
if (i == argc)
{
fprintf(stderr, "[error] Expecting a path after -tmpdir\n");
usage();
exit(1);
}
snprintf(tmpdir, sizeof(tmpdir), "%s", argv[i]);
}
else if (strcmp(arg, "-resolution") == 0)
{
i += 1;
if (i == argc)
{
fprintf(stderr, "[error] Expecting a number after -resolution\n");
usage();
exit(1);
}
default_resolution = atof(argv[i]);
if (default_resolution <= 0) default_resolution = 2.5f;
}
else
{
fprintf(stderr, "[error] Unknown option %s\n", arg);
Expand Down Expand Up @@ -283,43 +319,59 @@ int main(int argc, const char **argv)
bool init = 0;

//Initialize SDL
if (init == 0 && SDL_Init(SDL_INIT_VIDEO) < 0)
if (webview_mode)
{
fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
abort();
if (init == 0 && SDL_Init(SDL_INIT_TIMER | SDL_INIT_EVENTS) < 0)
{
fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
abort();
}
}
else
{
if (init == 0 && SDL_Init(SDL_INIT_VIDEO) < 0)
{
fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
abort();
}
}

custom_event = SDL_RegisterEvents(1);
signal(SIGUSR1, signal_usr1);

//Create window
char window_title[128] = "TeXpresso ";
strcat(window_title, doc_name);
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;

if (!webview_mode)
{
//Create window
char window_title[128];
snprintf(window_title, sizeof(window_title), "TeXpresso %s", doc_name);

#if SDL_VERSION_ATLEAST(2, 0, 8)
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
#endif

SDL_Window *window;
window = SDL_CreateWindow(window_title,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
700, 900,
SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE
);
window = SDL_CreateWindow(window_title,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
700, 900,
SDL_WINDOW_HIDDEN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE
);

if (window == NULL)
{
fprintf(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
abort();
}
if (window == NULL)
{
fprintf(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
abort();
}

SDL_Surface *logo = texpresso_logo();
fprintf(stderr, "texpresso logo: %dx%d\n", logo->w, logo->h);
SDL_SetWindowIcon(window, logo);
SDL_FreeSurface(logo);
SDL_Surface *logo = texpresso_logo();
fprintf(stderr, "texpresso logo: %dx%d\n", logo->w, logo->h);
SDL_SetWindowIcon(window, logo);
SDL_FreeSurface(logo);

SDL_Renderer *renderer;
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE);
SDL_ShowWindow(window);
}

struct persistent_state pstate = {
.initial = {0,},
Expand All @@ -339,8 +391,13 @@ int main(int argc, const char **argv)
.use_tectonic = use_tectonic,
.use_texlive = use_texlive,
.initialize_only = initialize_only,
.stream_mode = stream_mode
.stream_mode = stream_mode,
.webview_mode = webview_mode,
.default_resolution = default_resolution,
.render_width = 0,
.render_height = 0,
};
memcpy(pstate.tmpdir, tmpdir, sizeof(tmpdir));

int exit_code = 0;

Expand All @@ -355,8 +412,10 @@ int main(int argc, const char **argv)
;
}

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
if (!webview_mode) {
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
}
SDL_Quit();
fz_drop_context(ctx);

Expand Down
7 changes: 7 additions & 0 deletions src/frontend/driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ struct persistent_state {
const char *exe_path, *doc_path, *doc_name, *inclusion_path;

bool line_output, use_tectonic, use_texlive, initialize_only, stream_mode;

bool webview_mode;
bool dark_mode;
float default_resolution;
int render_width;
int render_height;
char tmpdir[4096];
};

bool texpresso_main(struct persistent_state *ps);
Expand Down
54 changes: 54 additions & 0 deletions src/frontend/editor.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,60 @@ bool editor_parse(fz_context *ctx,
goto arity;
*out = (struct editor_command){.tag = EDIT_INVERT, .invert = {}};
}
else if (strcmp(verb, "synctex-backward") == 0)
{
if (len != 4) goto arity;
*out = (struct editor_command){
.tag = EDIT_SYNCTEX_BACKWARD,
.synctex_backward = {
.page = val_number(ctx, val_array_get(ctx, stack, command, 1)),
.x = val_number(ctx, val_array_get(ctx, stack, command, 2)),
.y = val_number(ctx, val_array_get(ctx, stack, command, 3)),
}};
}
else if (strcmp(verb, "set-page") == 0)
{
if (len != 2) goto arity;
*out = (struct editor_command){
.tag = EDIT_SET_PAGE,
.set_page = {
.page = val_number(ctx, val_array_get(ctx, stack, command, 1)),
}};
}
else if (strcmp(verb, "set-output-size") == 0)
{
if (len != 3) goto arity;
*out = (struct editor_command){
.tag = EDIT_SET_OUTPUT_SIZE,
.set_output_size = {
.width = val_number(ctx, val_array_get(ctx, stack, command, 1)),
.height = val_number(ctx, val_array_get(ctx, stack, command, 2)),
}};
}
else if (strcmp(verb, "go-home") == 0)
{
if (len != 1) goto arity;
*out = (struct editor_command){.tag = EDIT_GO_HOME, .go_home = {}};
}
else if (strcmp(verb, "go-end") == 0)
{
if (len != 1) goto arity;
*out = (struct editor_command){.tag = EDIT_GO_END, .go_end = {}};
}
else if (strcmp(verb, "reset-zoom") == 0)
{
if (len != 1) goto arity;
*out = (struct editor_command){.tag = EDIT_RESET_ZOOM, .reset_zoom = {}};
}
else if (strcmp(verb, "set-fit-mode") == 0)
{
if (len != 2) goto arity;
val mode = val_array_get(ctx, stack, command, 1);
if (!val_is_string(mode)) goto arguments;
*out = (struct editor_command){.tag = EDIT_SET_FIT_MODE};
const char *s = val_string(ctx, stack, mode);
snprintf(out->set_fit_mode.mode, sizeof(out->set_fit_mode.mode), "%s", s);
}
else
{
fprintf(stderr, "[command] unknown verb: %s\n", verb);
Expand Down
33 changes: 33 additions & 0 deletions src/frontend/editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ enum EDITOR_COMMAND
EDIT_UNMAP_WINDOW,
EDIT_CROP,
EDIT_INVERT,
EDIT_SYNCTEX_BACKWARD,
EDIT_SET_PAGE,
EDIT_SET_OUTPUT_SIZE,
EDIT_GO_HOME,
EDIT_GO_END,
EDIT_RESET_ZOOM,
EDIT_SET_FIT_MODE,
};

struct editor_change
Expand Down Expand Up @@ -114,6 +121,32 @@ struct editor_command

struct {
} invert;

struct {
int page;
float x, y;
} synctex_backward;

struct {
int page;
} set_page;

struct {
int width, height;
} set_output_size;

struct {
} go_home;

struct {
} go_end;

struct {
} reset_zoom;

struct {
char mode[8];
} set_fit_mode;
};
};

Expand Down
Loading