Skip to content

Commit b5093c8

Browse files
committed
Merge branch 'dev' into release
2 parents 6a89be1 + 4071e74 commit b5093c8

15 files changed

+236
-69
lines changed

CMakeLists.txt

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
cmake_minimum_required (VERSION 3.9)
1919

2020
project (Lagrange
21-
VERSION 0.12.0
21+
VERSION 0.12.1
2222
DESCRIPTION "A Beautiful Gemini Client"
2323
LANGUAGES C
2424
)
@@ -30,6 +30,7 @@ option (ENABLE_X11_SWRENDER "Use software rendering under X11" OFF)
3030
option (ENABLE_KERNING "Enable kerning in font renderer (slower)" ON)
3131
option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF)
3232
option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF)
33+
option (ENABLE_IDLE_SLEEP "While idle, sleep in the main thread instead of waiting for events" ON)
3334

3435
include (BuildType.cmake)
3536
include (res/Embed.cmake)
@@ -219,6 +220,9 @@ if (ENABLE_MPG123 AND MPG123_FOUND)
219220
target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_MPG123=1)
220221
target_link_libraries (app PUBLIC PkgConfig::MPG123)
221222
endif ()
223+
if (ENABLE_IDLE_SLEEP)
224+
target_compile_definitions (app PUBLIC LAGRANGE_IDLE_SLEEP=1)
225+
endif ()
222226
target_link_libraries (app PUBLIC the_Foundation::the_Foundation)
223227
target_link_libraries (app PUBLIC ${SDL2_LDFLAGS})
224228
if (APPLE)

debian/rules

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export DEB_BUILD_MAINT_OPTIONS=hardening=-format
66
dh $@
77

88
override_dh_auto_configure:
9-
cmake .. -DENABLE_WINDOWPOS_FIX=YES -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$(pwd)/../usr
9+
cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_WINDOWPOS_FIX=YES -DTFDN_ENABLE_SSE41=NO -DCMAKE_INSTALL_PREFIX=$(pwd)/../usr
1010

1111
override_dh_build_configure:
1212
cmake --build .

lib/the_Foundation

Submodule the_Foundation updated from 17fd227 to 4514a18

res/MacOSXBundleInfo.plist.in

+11-11
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,16 @@
5454
<false/>
5555
</dict>
5656
</array>
57-
<key>CFBundleURLTypes</key>
58-
<array>
59-
<dict>
60-
<key>CFBundleURLName</key>
61-
<string>Gemini</string>
62-
<key>CFBundleURLSchemes</key>
63-
<array>
64-
<string>gemini</string>
65-
</array>
66-
</dict>
67-
</array>
57+
<key>CFBundleURLTypes</key>
58+
<array>
59+
<dict>
60+
<key>CFBundleURLName</key>
61+
<string>Gemini</string>
62+
<key>CFBundleURLSchemes</key>
63+
<array>
64+
<string>gemini</string>
65+
</array>
66+
</dict>
67+
</array>
6868
</dict>
6969
</plist>

res/about/help.gmi

+8-9
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Lagrange's user interface is modeled after web browsers:
6363

6464
* There is a navigation bar at the top with Back and Forward buttons.
6565
* There is a tab bar for switching tabs. The tab bar is hidden if there is only one tab open.
66-
* There is a sidebar for managing bookmarks and TLS identities, and viewing history and the page outline. The sidebar is hidden by default.
66+
* There is a sidebar for managing bookmarks and TLS identities, and viewing history and the page outline. Sidebars are hidden by default.
6767
* There is a search bar that appears at the bottom when searching text on the page.
6868

6969
Tip: Try pressing ${CTRL+}5 now to see the page outline.
@@ -126,17 +126,17 @@ Press ${CTRL+}T to open a new tab, and ${CTRL+}W to close the current tab. Right
126126

127127
The set of open tabs is restored when you launch Lagrange.
128128

129-
## Sidebar
129+
## Sidebars
130130

131-
The sidebar can be toggled via menus or by pressing ${SHIFT+}${CTRL+}L. It has five tabs:
131+
The sidebars can be toggled via menus or by pressing ${SHIFT+}${CTRL+}L or ${SHIFT+}${CTRL+}P (for the left/right sidebar, respectively). Both sidebars have five tabs:
132132

133133
* Bookmarks: List of bookmarks that you've created. These appear first in search results for quick and easy access.
134-
* Feeds: Entries found on subscribed feed index pages.
134+
* Feeds: Entries found on subscribed pages.
135135
* History: Chronological list of visited URLs. This is not a full history of all the URLs you've accessed over time — only unique URLs are shown at the latest access time.
136136
* Identities: TLS client certificates.
137137
* Outline: List of the headings in the currently open tab. Useful when reading longer documents.
138138

139-
${CTRL+}1 through ${CTRL+}5 switch between the sidebar tabs, or hide the sidebar if the current tab's key is pressed. You can also press Escape to dismiss the sidebar.
139+
${CTRL+}1 through ${CTRL+}5 switch between the left sidebar tabs, or hide the sidebar if the current tab's key is pressed. You can also press Escape to dismiss sidebars.
140140

141141
## Bookmarks
142142

@@ -150,10 +150,11 @@ In addition to a title, bookmarks can have tags. Some tags have a special meanin
150150

151151
* Set a "homepage" tag on a bookmark to make it one of the pages that will be opened when pressing the 🏠 button. If multiple bookmarks are tagged as homepages, a random one is selected.
152152
* A "subscribed" tag means that Lagrange will periodically fetch the bookmarked page and look for Gemini feed links. All the found links that match the required style ("YYYY-MM-DD Entry title") will appear in the Feeds sidebar tab.
153+
* "headings" can be used together with "subscribed" to subscribe to new headings instead of Gemini feed links.
153154

154155
## Feeds
155156

156-
You may be familiar with RSS and Atom XML feeds from the web. Lagrange does _not_ support RSS/Atom, only Gemini feeds. A Gemini feed is simply a regular 'text/gemini' page that contains one or more links whose labels are formatted in a particular way.
157+
You may be familiar with RSS and Atom XML feeds from the web. Lagrange does _not_ support RSS or Atom, only Gemini feeds. A Gemini feed is simply a regular 'text/gemini' page that contains one or more links whose labels are formatted in a particular way.
157158

158159
=> gemini://gemini.circumlunar.space/docs/companion/subscription.gmi See "Subscribing to Gemini pages" for more information.
159160

@@ -213,8 +214,6 @@ You can find a number of settings in Preferences to customize the user interface
213214

214215
### Browsing behavior
215216

216-
The "Outline on scrollbar" option shows the page outline when the mouse cursor is moved over the scrollbar, allowing one to better navigate long documents. The outline will disappear after the mouse cursor is moved away. If you need a persistent outline, one is always available in the sidebar.
217-
218217
One important characteristic of Gemini is that you remain in control of what gets loaded and when. The browser will not suddenly fetch a ton of images, autoplay videos, or make surreptitious connections to any tracking servers — each network request is purposeful and manually triggered. With this in mind, the "Load image on scroll" option is provided to assist keyboard-only browsing and to facilitate a focused reading experience. When enabled, if there is an image link visible on the page and you press Space or ↓, it will be loaded and shown inline _instead_ of the view scrolling down. This allows you to read and see all the content of the page while only tapping on a single key on the keyboard.
219218

220219
### Window and UI options
@@ -338,7 +337,7 @@ Example use cases:
338337

339338
## Interface
340339

341-
When a hook is called, it is given the MIME type and parameters via command line arguments. The response body is provided via stdin.
340+
When a hook is called, it is given the MIME type and parameters via command line arguments. The response body is provided via stdin. The request's URL is available in the REQUEST_URL environment variable.
342341

343342
The MIME type and parameters are split at semicolons, so "text/gemini; lang=ja" would be called as:
344343
```

res/about/version.gmi

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@
66
```
77
# Release notes
88

9+
## 0.12.1
10+
* 'text/*' content falls back to plain text.
11+
* Minimized visual artifacts in Unicode box-drawing characters (overlapping/gaps) by fine-tuning glyph scaling.
12+
* Fixed truncated tab titles when opening tabs in background.
13+
* Fixed possible exit if hook program not found (SIGPIPE).
14+
* REQUEST_URL is set in the environment when running MIME hooks.
15+
* "about:debug" lists the configured MIME hooks.
16+
* macOS: Fixed excessive CPU usage while idling.
17+
918
## 0.12
1019
* Added MIME hooks: pipe Gemini responses through external programs for arbitrary processing. (See "about:help" for usage.)
1120
* Added a right-hand sidebar; have a sidebar on the right or on both sides at once.

src/app.c

+80-10
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ static const char *prefsFileName_App_ = "prefs.cfg";
8989
static const char *stateFileName_App_ = "state.binary";
9090
static const char *downloadDir_App_ = "~/Downloads";
9191

92+
static const int idleThreshold_App_ = 1000; /* ms */
93+
9294
struct Impl_App {
9395
iCommandLine args;
9496
iString * execPath;
@@ -100,7 +102,12 @@ struct Impl_App {
100102
iSortedArray tickers;
101103
uint32_t lastTickerTime;
102104
uint32_t elapsedSinceLastTicker;
103-
iBool running;
105+
iBool isRunning;
106+
#if defined (LAGRANGE_IDLE_SLEEP)
107+
iBool isIdling;
108+
uint32_t lastEventTime;
109+
int sleepTimer;
110+
#endif
104111
iAtomicInt pendingRefresh;
105112
int tabEnum;
106113
iStringList *launchCommands;
@@ -320,6 +327,16 @@ static void saveState_App_(const iApp *d) {
320327
iRelease(f);
321328
}
322329

330+
#if defined (LAGRANGE_IDLE_SLEEP)
331+
static uint32_t checkAsleep_App_(uint32_t interval, void *param) {
332+
iApp *d = param;
333+
SDL_Event ev = { .type = SDL_USEREVENT };
334+
ev.user.code = asleep_UserEventCode;
335+
SDL_PushEvent(&ev);
336+
return interval;
337+
}
338+
#endif
339+
323340
static void init_App_(iApp *d, int argc, char **argv) {
324341
const iBool isFirstRun = !fileExistsCStr_FileInfo(cleanedPath_CStr(dataDir_App_));
325342
d->isFinishedLaunching = iFalse;
@@ -349,7 +366,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
349366
#endif
350367
init_Prefs(&d->prefs);
351368
setCStr_String(&d->prefs.downloadDir, downloadDir_App_);
352-
d->running = iFalse;
369+
d->isRunning = iFalse;
353370
d->window = NULL;
354371
set_Atomic(&d->pendingRefresh, iFalse);
355372
d->mimehooks = new_MimeHooks();
@@ -358,6 +375,11 @@ static void init_App_(iApp *d, int argc, char **argv) {
358375
d->bookmarks = new_Bookmarks();
359376
d->tabEnum = 0; /* generates unique IDs for tab pages */
360377
setThemePalette_Color(d->prefs.theme);
378+
#if defined (LAGRANGE_IDLE_SLEEP)
379+
d->isIdling = iFalse;
380+
d->lastEventTime = 0;
381+
d->sleepTimer = SDL_AddTimer(1000, checkAsleep_App_, d);
382+
#endif
361383
#if defined (iPlatformApple)
362384
setupApplication_MacOS();
363385
#endif
@@ -480,23 +502,31 @@ const iString *debugInfo_App(void) {
480502
iConstForEach(StringList, j, d->launchCommands) {
481503
appendFormat_String(msg, "%s\n", cstr_String(j.value));
482504
}
505+
appendFormat_String(msg, "## MIME hooks\n");
506+
append_String(msg, debugInfo_MimeHooks(d->mimehooks));
483507
return msg;
484508
}
485509

486510
iLocalDef iBool isWaitingAllowed_App_(iApp *d) {
511+
#if defined (LAGRANGE_IDLE_SLEEP)
512+
if (d->isIdling) {
513+
return iFalse;
514+
}
515+
#endif
487516
return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers);
488517
}
489518

490519
void processEvents_App(enum iAppEventMode eventMode) {
491520
iApp *d = &app_;
492521
SDL_Event ev;
522+
iBool gotEvents = iFalse;
493523
while ((isWaitingAllowed_App_(d) && eventMode == waitForNewEvents_AppEventMode &&
494524
SDL_WaitEvent(&ev)) ||
495525
((!isWaitingAllowed_App_(d) || eventMode == postedEventsOnly_AppEventMode) &&
496526
SDL_PollEvent(&ev))) {
497527
switch (ev.type) {
498528
case SDL_QUIT:
499-
d->running = iFalse;
529+
d->isRunning = iFalse;
500530
goto backToMainLoop;
501531
case SDL_DROPFILE: {
502532
iBool newTab = iFalse;
@@ -516,6 +546,25 @@ void processEvents_App(enum iAppEventMode eventMode) {
516546
break;
517547
}
518548
default: {
549+
#if defined (LAGRANGE_IDLE_SLEEP)
550+
if (ev.type == SDL_USEREVENT && ev.user.code == asleep_UserEventCode) {
551+
if (SDL_GetTicks() - d->lastEventTime > idleThreshold_App_) {
552+
if (!d->isIdling) {
553+
// printf("[App] idling...\n");
554+
fflush(stdout);
555+
}
556+
d->isIdling = iTrue;
557+
}
558+
continue;
559+
}
560+
d->lastEventTime = SDL_GetTicks();
561+
if (d->isIdling) {
562+
// printf("[App] ...woke up\n");
563+
fflush(stdout);
564+
}
565+
d->isIdling = iFalse;
566+
#endif
567+
gotEvents = iTrue;
519568
iBool wasUsed = processEvent_Window(d->window, &ev);
520569
if (!wasUsed) {
521570
/* There may be a key bindings for this. */
@@ -539,6 +588,14 @@ void processEvents_App(enum iAppEventMode eventMode) {
539588
}
540589
}
541590
}
591+
#if defined (LAGRANGE_IDLE_SLEEP)
592+
if (d->isIdling && !gotEvents) {
593+
/* This is where we spend most of our time when idle. 60 Hz still quite a lot but we
594+
can't wait too long after the user tries to interact again with the app. In any
595+
case, on macOS SDL_WaitEvent() seems to use 10x more CPU time than sleeping. */
596+
SDL_Delay(1000 / 60);
597+
}
598+
#endif
542599
backToMainLoop:;
543600
}
544601

@@ -586,10 +643,10 @@ static int resizeWatcher_(void *user, SDL_Event *event) {
586643

587644
static int run_App_(iApp *d) {
588645
arrange_Widget(findWidget_App("root"));
589-
d->running = iTrue;
646+
d->isRunning = iTrue;
590647
SDL_EventState(SDL_DROPFILE, SDL_ENABLE); /* open files via drag'n'drop */
591648
SDL_AddEventWatch(resizeWatcher_, d);
592-
while (d->running) {
649+
while (d->isRunning) {
593650
processEvents_App(waitForNewEvents_AppEventMode);
594651
runTickers_App_(d);
595652
refresh_App();
@@ -600,6 +657,9 @@ static int run_App_(iApp *d) {
600657

601658
void refresh_App(void) {
602659
iApp *d = &app_;
660+
#if defined (LAGRANGE_IDLE_SLEEP)
661+
if (d->isIdling) return;
662+
#endif
603663
destroyPending_Widget();
604664
draw_Window(d->window);
605665
set_Atomic(&d->pendingRefresh, iFalse);
@@ -657,6 +717,9 @@ int run_App(int argc, char **argv) {
657717

658718
void postRefresh_App(void) {
659719
iApp *d = &app_;
720+
#if defined (LAGRANGE_IDLE_SLEEP)
721+
d->isIdling = iFalse;
722+
#endif
660723
const iBool wasPending = exchange_Atomic(&d->pendingRefresh, iTrue);
661724
if (!wasPending) {
662725
SDL_Event ev;
@@ -852,6 +915,7 @@ iDocumentWidget *newTab_App(const iDocumentWidget *duplicateOf, iBool switchToNe
852915
}
853916
arrange_Widget(tabs);
854917
refresh_Widget(tabs);
918+
postCommandf_App("tab.created id:%s", cstr_String(id_Widget(as_Widget(doc))));
855919
return doc;
856920
}
857921

@@ -972,7 +1036,13 @@ iBool willUseProxy_App(const iRangecc scheme) {
9721036

9731037
iBool handleCommand_App(const char *cmd) {
9741038
iApp *d = &app_;
975-
if (equal_Command(cmd, "prefs.dialogtab")) {
1039+
if (equal_Command(cmd, "config.error")) {
1040+
makeMessage_Widget(uiTextCaution_ColorEscape "CONFIG ERROR",
1041+
format_CStr("Error in config file: %s\nSee \"about:debug\" for details.",
1042+
suffixPtr_Command(cmd, "where")));
1043+
return iTrue;
1044+
}
1045+
else if (equal_Command(cmd, "prefs.dialogtab")) {
9761046
d->prefs.dialogTab = arg_Command(cmd);
9771047
return iTrue;
9781048
}
@@ -1084,12 +1154,12 @@ iBool handleCommand_App(const char *cmd) {
10841154
}
10851155
else if (equal_Command(cmd, "prefs.sideicon.changed")) {
10861156
d->prefs.sideIcon = arg_Command(cmd) != 0;
1087-
refresh_App();
1157+
postRefresh_App();
10881158
return iTrue;
10891159
}
10901160
else if (equal_Command(cmd, "prefs.hoveroutline.changed")) {
10911161
d->prefs.hoverOutline = arg_Command(cmd) != 0;
1092-
refresh_App();
1162+
postRefresh_App();
10931163
return iTrue;
10941164
}
10951165
else if (equal_Command(cmd, "saturation.set")) {
@@ -1372,13 +1442,13 @@ iBool handleCommand_App(const char *cmd) {
13721442
}
13731443
else if (equal_Command(cmd, "feeds.update.started")) {
13741444
setFlags_Widget(findWidget_App("feeds.progress"), hidden_WidgetFlag, iFalse);
1375-
refresh_App();
1445+
postRefresh_App();
13761446
return iFalse;
13771447
}
13781448
else if (equal_Command(cmd, "feeds.update.finished")) {
13791449
setFlags_Widget(findWidget_App("feeds.progress"), hidden_WidgetFlag, iTrue);
13801450
refreshFinished_Feeds();
1381-
refresh_App();
1451+
postRefresh_App();
13821452
return iFalse;
13831453
}
13841454
else if (equal_Command(cmd, "visited.changed")) {

src/app.h

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ enum iAppEventMode {
4646
enum iUserEventCode {
4747
command_UserEventCode = 1,
4848
refresh_UserEventCode = 2,
49+
asleep_UserEventCode = 3,
4950
};
5051

5152
const iString *execPath_App (void);

src/gmrequest.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,9 @@ static void requestFinished_GmRequest_(iGmRequest *d, iTlsRequest *req) {
262262
checkServerCertificate_GmRequest_(d);
263263
unlock_Mutex(d->mtx);
264264
/* Check for mimehooks. */
265-
if (d->isRespFiltered && d->state == finished_GmRequestState) {
266-
iBlock *xbody = tryFilter_MimeHooks(mimeHooks_App(), &d->resp->meta, &d->resp->body);
265+
if (d->isRespFiltered && d->state == finished_GmRequestState) {
266+
iBlock *xbody =
267+
tryFilter_MimeHooks(mimeHooks_App(), &d->resp->meta, &d->resp->body, &d->url);
267268
if (xbody) {
268269
lock_Mutex(d->mtx);
269270
clear_String(&d->resp->meta);

0 commit comments

Comments
 (0)