From be2efda0d56d5cfd6bd377adc440736a326903f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaakko=20Kera=CC=88nen?= jaakko.keranen@iki.fi
Date: Sun, 18 Apr 2021 14:03:05 +0300
Subject: [PATCH 1/1] Added build option to disable IPC
It may take a while to add D-Bus support, so adding a way to disable the incompatible IPC mechanism for Flatpak builds.
IssueID #245
CMakeLists.txt | 18 ++++++++++-----
src/app.c | 38 ++++++++++++++++++-------------
src/ipc.h | 3 +++
src/prefs.h | 2 +-
src/ui/util.c | 4 ++--
src/ui/window.c | 60 +++++++++++++++++++++++++------------------------
src/ui/window.h | 4 ++--
src/win32.c | 4 ++--
src/win32.h | 2 +-
9 files changed, 78 insertions(+), 57 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e0d12d4d..1db57832 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,6 +26,7 @@ set (IOS_BUNDLE_VERSION 7)
set (COPYRIGHT_YEAR 2021)
+option (ENABLE_IPC "Use IPC to communicate between running instances" ON)
option (ENABLE_MPG123 "Use mpg123 for decoding MPEG audio" ON)
option (ENABLE_X11_SWRENDER "Use software rendering under X11" OFF)
option (ENABLE_KERNING "Enable kerning in font renderer (slower)" ON)
@@ -115,8 +116,6 @@ set (SOURCES
src/gopher.h
src/history.c
src/history.h
src/lang.c
src/lang.h
src/lookup.c
@@ -198,6 +197,12 @@ set (SOURCES
${CMAKE_CURRENT_BINARY_DIR}/embedded.c
${CMAKE_CURRENT_BINARY_DIR}/embedded.h
)
+if (ENABLE_IPC)
src/ipc.c
src/ipc.h
+endif ()
if (ANDROID)
set (MOBILE 1)
add_definitions (-DiPlatformAndroidMobile=1)
@@ -254,6 +259,9 @@ target_compile_options (app PUBLIC
-DSTB_VORBIS_NO_INTEGER_CONVERSION=1
)
target_compile_definitions (app PUBLIC LAGRANGE_APP_VERSION="${PROJECT_VERSION}")
+if (ENABLE_IPC)
+endif ()
if (ENABLE_X11_SWRENDER)
target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_X11_SWRENDER=1)
endif ()
@@ -268,13 +276,13 @@ if (ENABLE_MPG123 AND MPG123_FOUND)
target_link_libraries (app PUBLIC PkgConfig::MPG123)
endif ()
if (ENABLE_IDLE_SLEEP)
endif ()
if (ENABLE_DOWNLOAD_EDIT)
endif ()
if (ENABLE_CUSTOM_FRAME AND MSYS)
endif ()
target_link_libraries (app PUBLIC the_Foundation::the_Foundation)
target_link_libraries (app PUBLIC ${SDL2_LDFLAGS})
diff --git a/src/app.c b/src/app.c
index 5f8bdade..3792d35c 100644
--- a/src/app.c
+++ b/src/app.c
@@ -117,7 +117,7 @@ struct Impl_App {
uint32_t lastTickerTime;
uint32_t elapsedSinceLastTicker;
iBool isRunning;
-#if defined (LAGRANGE_IDLE_SLEEP)
+#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
iBool isIdling;
uint32_t lastEventTime;
int sleepTimer;
@@ -169,7 +169,7 @@ static iString *serializePrefs_App_(const iApp *d) {
iString *str = new_String();
const iSidebarWidget *sidebar = findWidget_App("sidebar");
const iSidebarWidget *sidebar2 = findWidget_App("sidebar2");
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
appendFormat_String(str, "customframe arg:%d\n", d->prefs.customFrame);
#endif
appendFormat_String(str, "window.retain arg:%d\n", d->prefs.retainWindowSize);
@@ -184,7 +184,7 @@ static iString *serializePrefs_App_(const iApp *d) {
appendFormat_String(str, "sidebar2.width arg:%f gaps:1\n", width_SidebarWidget(sidebar2));
/* On macOS, maximization should be applied at creation time or the window will take
a moment to animate to its maximized size. */
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
if (snap_Window(d->window)) {
if (~SDL_GetWindowFlags(d->window->win) & SDL_WINDOW_MINIMIZED) {
/* Save the actual visible window position, too, because snapped windows may
@@ -337,7 +337,7 @@ static void loadPrefs_App_(iApp *d) {
d->initialWindowRect = init_Rect(
pos.x, pos.y, argLabel_Command(cmd, "width"), argLabel_Command(cmd, "height"));
}
-#if !defined (LAGRANGE_DOWNLOAD_EDIT)
+#if !defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT)
else if (equal_Command(cmd, "downloads")) {
continue; /* can't change downloads directory */
}
@@ -353,7 +353,7 @@ static void loadPrefs_App_(iApp *d) {
/* Default CA setup. */
setCACertificates_TlsRequest(&d->prefs.caFile, &d->prefs.caPath);
}
-#if !defined (LAGRANGE_CUSTOM_FRAME)
+#if !defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
d->prefs.customFrame = iFalse;
#endif
iRelease(f);
@@ -447,7 +447,7 @@ static void saveState_App_(const iApp *d) {
iRelease(f);
}
-#if defined (LAGRANGE_IDLE_SLEEP)
+#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
static uint32_t checkAsleep_App_(uint32_t interval, void *param) {
iApp *d = param;
iUnused(d);
@@ -470,6 +470,7 @@ static void terminate_App_(int rc) {
exit(rc);
}
+#if defined (LAGRANGE_ENABLE_IPC)
static void communicateWithRunningInstance_App_(iApp *d, iProcessId instance,
const iStringList *openCmds) {
iString *cmds = new_String();
@@ -520,6 +521,7 @@ static void communicateWithRunningInstance_App_(iApp *d, iProcessId instance,
// }
terminate_App_(0);
}
+#endif /* defined (LAGRANGE_ENABLE_IPC) */
static void init_App_(iApp *d, int argc, char **argv) {
init_CommandLine(&d->args, argc, argv);
@@ -600,6 +602,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
}
}
}
+#if defined (LAGRANGE_ENABLE_IPC)
/* Only one instance is allowed to run at a time; the runtime files (bookmarks, etc.)
are not shareable. */ {
init_Ipc(dataDir_App_());
@@ -614,6 +617,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
}
listen_Ipc(); /* We'll respond to commands from other instances. */
}
+#endif
printf("Lagrange: A Beautiful Gemini Client\n");
const iBool isFirstRun =
!fileExistsCStr_FileInfo(cleanedPath_CStr(concatPath_CStr(dataDir_App_(), "prefs.cfg")));
@@ -686,7 +690,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
postCommand_App("window.unfreeze");
d->autoReloadTimer = SDL_AddTimer(60 * 1000, postAutoReloadCommand_App_, NULL);
postCommand_App("document.autoreload");
-#if defined (LAGRANGE_IDLE_SLEEP)
+#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
d->isIdling = iFalse;
d->lastEventTime = 0;
d->sleepTimer = SDL_AddTimer(1000, checkAsleep_App_, d);
@@ -707,7 +711,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
}
static void deinit_App(iApp *d) {
-#if defined (LAGRANGE_IDLE_SLEEP)
+#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
SDL_RemoveTimer(d->sleepTimer);
#endif
SDL_RemoveTimer(d->autoReloadTimer);
@@ -729,7 +733,9 @@ static void deinit_App(iApp *d) {
deinit_CommandLine(&d->args);
iRelease(d->launchCommands);
delete_String(d->execPath);
+#if defined (LAGRANGE_ENABLE_IPC)
deinit_Ipc();
+#endif
deinit_SortedArray(&d->tickers);
deinit_Periodic(&d->periodic);
deinit_Lang();
@@ -875,7 +881,7 @@ iLocalDef iBool isWaitingAllowed_App_(iApp *d) {
if (d->warmupFrames > 0) {
return iFalse;
}
-#if defined (LAGRANGE_IDLE_SLEEP)
+#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
if (d->isIdling) {
return iFalse;
}
@@ -928,7 +934,7 @@ void processEvents_App(enum iAppEventMode eventMode) {
case SDL_APP_DIDENTERFOREGROUND:
gotEvents = iTrue;
d->warmupFrames = 5;
-#if defined (LAGRANGE_IDLE_SLEEP)
+#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
d->isIdling = iFalse;
d->lastEventTime = SDL_GetTicks();
#endif
@@ -961,7 +967,7 @@ void processEvents_App(enum iAppEventMode eventMode) {
break;
}
default: {
-#if defined (LAGRANGE_IDLE_SLEEP)
+#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
if (ev.type == SDL_USEREVENT && ev.user.code == asleep_UserEventCode) {
if (SDL_GetTicks() - d->lastEventTime > idleThreshold_App_ &&
isEmpty_SortedArray(&d->tickers)) {
@@ -1050,7 +1056,7 @@ void processEvents_App(enum iAppEventMode eventMode) {
}
}
}
-#if defined (LAGRANGE_IDLE_SLEEP)
+#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
if (d->isIdling && !gotEvents && isFinished_Anim(&d->window->rootOffset)) {
/* This is where we spend most of our time when idle. 60 Hz still quite a lot but we
can't wait too long after the user tries to interact again with the app. In any
@@ -1123,7 +1129,7 @@ static int run_App_(iApp *d) {
void refresh_App(void) {
iApp *d = &app_;
destroyPending_Widget();
-#if defined (LAGRANGE_IDLE_SLEEP)
+#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
if (d->warmupFrames == 0 && d->isIdling) {
return;
}
@@ -1200,7 +1206,7 @@ int run_App(int argc, char **argv) {
void postRefresh_App(void) {
iApp *d = &app_;
-#if defined (LAGRANGE_IDLE_SLEEP)
+#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
d->isIdling = iFalse;
#endif
const iBool wasPending = exchange_Atomic(&d->pendingRefresh, iTrue);
@@ -1341,7 +1347,7 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) {
if (equal_Command(cmd, "prefs.dismiss") || equal_Command(cmd, "preferences")) {
setUiScale_Window(get_Window(),
toFloat_String(text_InputWidget(findChild_Widget(d, "prefs.uiscale"))));
-#if defined (LAGRANGE_DOWNLOAD_EDIT)
+#if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT)
postCommandf_App("downloads path:%s",
cstr_String(text_InputWidget(findChild_Widget(d, "prefs.downloads"))));
#endif
@@ -2140,6 +2146,7 @@ iBool handleCommand_App(const char *cmd) {
}
return iFalse;
}
+#if defined (LAGRANGE_ENABLE_IPC)
else if (equal_Command(cmd, "ipc.list.urls")) {
iProcessId pid = argLabel_Command(cmd, "pid");
if (pid) {
@@ -2156,6 +2163,7 @@ iBool handleCommand_App(const char *cmd) {
signal_Ipc(arg_Command(cmd));
return iTrue;
}
+#endif /* defined (LAGRANGE_ENABLE_IPC) */
else {
return iFalse;
}
diff --git a/src/ipc.h b/src/ipc.h
index 8127bf86..69852876 100644
--- a/src/ipc.h
+++ b/src/ipc.h
@@ -21,6 +21,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#pragma once
+#if defined (LAGRANGE_ENABLE_IPC)
#include <the_Foundation/string.h>
#include <the_Foundation/process.h>
@@ -39,3 +40,5 @@ enum iIpcWrite {
};
iBool write_Ipc (iProcessId pid, const iString *input, enum iIpcWrite type);
+#endif /* defined (LAGRANGE_ENABLE_IPC) */
diff --git a/src/prefs.h b/src/prefs.h
index 6be7fd41..160bb974 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -45,7 +45,7 @@ struct Impl_Prefs {
iBool useSystemTheme;
enum iColorTheme theme;
enum iColorAccent accent;
iBool retainWindowSize;
float uiScale;
int zoomPercent;
diff --git a/src/ui/util.c b/src/ui/util.c
index f56baa36..ddaccd68 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1936,7 +1936,7 @@ iWidget *makePreferences_Widget(void) {
const int bigGap = lineHeight_Text(uiLabel_FontId) * 3 / 4;
/* General preferences. */ {
appendTwoColumnPage_(tabs, "${heading.prefs.general}", '1', &headings, &values);
-#if defined (LAGRANGE_DOWNLOAD_EDIT)
+#if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT)
addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.downloads}")));
setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.downloads");
#endif
@@ -2014,7 +2014,7 @@ iWidget *makePreferences_Widget(void) {
}
addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.accent}")));
addChildFlags_Widget(values, iClob(accent), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.customframe}")));
addChild_Widget(values, iClob(makeToggle_Widget("prefs.customframe")));
#endif
diff --git a/src/ui/window.c b/src/ui/window.c
index bda645f7..a3633580 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -923,7 +923,7 @@ static void setupUserInterface_Window(iWindow *d) {
setId_Widget(div, "navdiv");
addChild_Widget(d->root, iClob(div));
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
/* Window title bar. */
if (prefs_App()->customFrame) {
setPadding1_Widget(div, 1);
@@ -1358,7 +1358,7 @@ static void drawBlank_Window_(iWindow *d) {
SDL_RenderPresent(d->render);
}
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
static SDL_HitTestResult hitTest_Window_(SDL_Window *win, const SDL_Point *pos, void *data) {
iWindow *d = data;
iAssert(d->win == win);
@@ -1411,7 +1411,7 @@ SDL_HitTestResult hitTest_Window(const iWindow *d, iInt2 pos) {
iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) {
flags |= SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN;
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
if (prefs_App()->customFrame) {
/* We are drawing a custom frame so hide the default one. */
flags |= SDL_WINDOW_BORDERLESS;
@@ -1421,7 +1421,7 @@ iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) {
width_Rect(rect), height_Rect(rect), flags, &d->win, &d->render)) {
return iFalse;
}
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
if (prefs_App()->customFrame) {
/* Register a handler for window hit testing (drag, resize). */
SDL_SetWindowHitTest(d->win, hitTest_Window_, d);
@@ -1540,7 +1540,7 @@ void init_Window(iWindow *d, iRect rect) {
SDL_FreeSurface(surf);
}
d->appIcon = NULL;
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
/* Load the app icon for drawing in the title bar. */
if (prefs_App()->customFrame) {
SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, appIconSize_());
@@ -1603,7 +1603,7 @@ static iBool isNormalPlacement_Window_(const iWindow *d) {
}
static iBool unsnap_Window_(iWindow *d, const iInt2 *newPos) {
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
if (!prefs_App()->customFrame) {
return iFalse;
}
@@ -1693,7 +1693,7 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
d->isMinimized = iTrue;
return iFalse;
}
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
/* Set the snap position depending on where the mouse cursor is. */
if (prefs_App()->customFrame) {
SDL_Rect usable;
@@ -1723,7 +1723,7 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
return iTrue;
}
}
-#endif /* defined LAGRANGE_CUSTOM_FRAME */
+#endif /* defined LAGRANGE_ENABLE_CUSTOM_FRAME */
//printf("MOVED: %d, %d\n", ev->data1, ev->data2); fflush(stdout);
if (unsnap_Window_(d, &newPos)) {
return iTrue;
@@ -1813,7 +1813,7 @@ static void applyCursor_Window_(iWindow *d) {
iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
switch (ev->type) {
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
case SDL_SYSWMEVENT: {
/* We observe native Win32 messages for better user interaction with the
window frame. Mouse clicks especially will not generate normal SDL
@@ -1966,25 +1966,27 @@ void draw_Window(iWindow *d) {
}
/* Draw widgets. */
d->frameTime = SDL_GetTicks();
-#if defined (LAGRANGE_CUSTOM_FRAME)
const int size = appIconSize_();
const iRect rect = bounds_Widget(appIcon);
const iInt2 mid = mid_Rect(rect);
const iBool isLight = isLight_ColorTheme(colorTheme_App());
iColor iconColor = get_Color(gotFocus || isLight ? white_ColorId : uiAnnotation_ColorId);
SDL_SetTextureColorMod(d->appIcon, iconColor.r, iconColor.g, iconColor.b);
SDL_SetTextureAlphaMod(d->appIcon, gotFocus || !isLight ? 255 : 92);
SDL_RenderCopy(
d->render,
d->appIcon,
NULL,
&(SDL_Rect){ left_Rect(rect) + gap_UI * 1.25f, mid.y - size / 2, size, size });
draw_Widget(d->root);
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
/* App icon. */
const iWidget *appIcon = findChild_Widget(d->root, "winbar.icon");
if (isVisible_Widget(appIcon)) {
const int size = appIconSize_();
const iRect rect = bounds_Widget(appIcon);
const iInt2 mid = mid_Rect(rect);
const iBool isLight = isLight_ColorTheme(colorTheme_App());
iColor iconColor = get_Color(gotFocus || isLight ? white_ColorId : uiAnnotation_ColorId);
SDL_SetTextureColorMod(d->appIcon, iconColor.r, iconColor.g, iconColor.b);
SDL_SetTextureAlphaMod(d->appIcon, gotFocus || !isLight ? 255 : 92);
SDL_RenderCopy(
d->render,
d->appIcon,
NULL,
&(SDL_Rect){ left_Rect(rect) + gap_UI * 1.25f, mid.y - size / 2, size, size });
}
#endif
#if 0
/* Text cache debugging. */ {
SDL_Texture *cache = glyphCache_Text();
@@ -2112,7 +2114,7 @@ void setSnap_Window(iWindow *d, int snapMode) {
}
return;
}
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
if (d->place.snap == snapMode) {
return;
}
@@ -2178,7 +2180,7 @@ void setSnap_Window(iWindow *d, int snapMode) {
arrange_Widget(d->root);
postRefresh_App();
}
-#endif /* defined (LAGRANGE_CUSTOM_FRAME) */
+#endif /* defined (LAGRANGE_ENABLE_CUSTOM_FRAME) */
}
int snap_Window(const iWindow *d) {
diff --git a/src/ui/window.h b/src/ui/window.h
index e3d8b22b..052f8828 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -52,7 +52,7 @@ struct Impl_WindowPlacement {
iInt2 initialPos;
iRect normalRect; /* updated when window is moved/resized */
iInt2 lastNotifiedSize; /* keep track of horizontal/vertical notifications */
int lastHit;
};
@@ -111,7 +111,7 @@ iBool isNarrow_Window (const iWindow *);
iWindow * get_Window (void);
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
SDL_HitTestResult hitTest_Window(const iWindow *d, iInt2 pos);
#endif
diff --git a/src/win32.c b/src/win32.c
index 01ec73bf..d040d8a9 100644
--- a/src/win32.c
+++ b/src/win32.c
@@ -64,7 +64,7 @@ void useExecutableIconResource_SDLWindow(SDL_Window *win) {
}
}
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
iInt2 cursor_Win32(void) {
POINT p;
GetPhysicalCursorPos(&p);
@@ -191,4 +191,4 @@ void processNativeEvent_Win32(const struct SDL_SysWMmsg *msg, iWindow *window) {
#endif
}
}
-#endif /* defined (LAGRANGE_CUSTOM_FRAME) */
+#endif /* defined (LAGRANGE_ENABLE_CUSTOM_FRAME) */
diff --git a/src/win32.h b/src/win32.h
index 27aa0539..d53a2fe8 100644
--- a/src/win32.h
+++ b/src/win32.h
@@ -32,7 +32,7 @@ void setDPIAware_Win32(void);
float desktopDPI_Win32(void);
void useExecutableIconResource_SDLWindow(SDL_Window *win);
-#if defined (LAGRANGE_CUSTOM_FRAME)
+#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
iInt2 cursor_Win32(void);
void processNativeEvent_Win32(const struct SDL_SysWMmsg *msg, iWindow *window);
void setup_SDLWindow(SDL_Window *);
--
2.25.1
text/plain
This content has been proxied by September (3851b).