``` "Lagrange" written in all caps __ __ __ ___ | /\ / _` |__) /\ |\ | / _` |__ |___ /~~\ \__> | \ /~~\ | \| \__> |___ ``` # Help ## What is Gemini Gemini is a simple protocol for serving content over the internet. It specifies a Markdown inspired format allowing basic plain text document markup. Compared to HTTP and HTML, Gemini is vastly simpler and easier to work with. => gemini://gemini.circumlunar.space/docs/faq.gmi Project Gemini FAQ => gemini://gemini.circumlunar.space/docs/specification.gmi Protocol and 'text/gemini' specification ## What is Lagrange Lagrange is a GUI client for browsing Geminispace. It offers modern conveniences familiar from web browsers, such as smooth scrolling, inline image viewing, multiple tabs, visual themes, Unicode fonts, bookmarks, history, and page outlines. Like Gemini, Lagrange has been designed with minimalism in mind. It depends on a small number of essential libraries. It is written in C and uses SDL for hardware-accelerated graphics. OpenSSL is used for secure communications. => about:lagrange About Lagrange => https://www.libsdl.org SDL: Simple DirectMedia Layer => https://www.openssl.org OpenSSL: Cryptography and SSL/TLS Toolkit ### Features * Beautiful typography using Unicode fonts * Autogenerated page style and Unicode icon for each Gemini domain * Smart suggestions when typing the URL — search bookmarks, history, identities * Sidebar for page outline, managing bookmarks and identities, and viewing history * Multiple tabs * Identity management — create and use TLS client certificates * Subscribe to Gemini and Atom feeds * Use Gemini pages as a source of bookmarks * Light and dark color themes * Select and copy text with the mouse * Find text on the page * Search engine integration * Open image links inline on the same page * Audio playback: MP3, Ogg Vorbis, WAV * Open links via keyboard shortcuts * Instant back/forward navigation * Smooth scrolling * Scaling page content (50%...200%) * Scaling factor for the UI (for arbitrary monitor DPI) * Persistent app state — tabs and history are restored on next run * Configurable keybindings * Built-in support for Gopher * Use proxy servers for HTTP, Gopher, or Gemini content ## Why not just use a web browser Modern web browsers are complex beasts. In fact, they are so complex that one can create a fully functional virtual machine inside one and run another operating system! => https://win95.ajf.me Windows 95 on DOSBox (using Emscripten) If one seeks to just read text and view images, this is absurd overkill. Having a universal platform that runs everywhere and on everything is clearly a valuable notion, but it comes with a hefty price tag. The software stack towers ever higher, and hardware needs to be ever more powerful and complicated to run it well. However, everything on the internet doesn't have to rely on this behemoth. One way to browse Gemini content is via web browser extensions or proxies that translate the content for the web. This may be a sufficient and easy solution for you. However, native clients such as Lagrange also benefit from the simpleness of the protocol and the content. The experience can be optimized, and the software runs well even on simple hardware like the Raspberry Pi. # 1 User interface Lagrange's user interface is modeled after web browsers: * There is a navigation bar at the top with Back and Forward buttons. * There is a tab bar for switching tabs. The tab bar is hidden if there is only one tab open. * There is a sidebar for managing bookmarks and TLS identities, and viewing history and the page outline. Sidebars are hidden by default. * There is a search bar that appears at the bottom when searching text on the page. Tip: Try pressing ${CTRL+}5 now to see the page outline. The user interface has been designed for both mouse and keyboard based use. When using a mouse the most commonly needed features are available via context menus, for example by right-clicking on the currently open page. Keyboard shortcuts on the other hand can provide a very quick and fluid user experience. For example, to open the "CAPCOM" bookmark, one might use the following keyboard shortcuts: * ${CTRL+}L to activate the URL input field * Type "cap" which shows the "CAPCOM" bookmark as the first lookup result * Tab (or ↓) to move focus to the lookup results * Enter to select "CAPCOM" ## 1.1 Navigation ### 1.1.1 URL entry and quick search The URL input field is in its typical location in the navigation bar. It can be accessed quickly by pressing ${CTRL+}L. You can then type or paste an URL and press Enter to load the page. As you enter text in the URL input field, Lagrange starts looking for matches in bookmarks, history, identities, and content of cached pages. This provides an easy way to return to a page you've been on recently. Press Tab or ↓ to switch input focus to the results. Search within cached pages is limited to the (small) set of pages that Lagrange keeps in memory for back navigation. Search terms are case insensitive, and if many words are entered, they are all required to appear in the specified order in any matched content. Note that the navigation stack is saved to a file when Lagrange is shut down and restored on the next launch. This means the next time you launch Lagrange, you can still search the contents of past pages. However, navigation stacks are tab-specific, so closing a tab will delete its history as well. You can also make online search queries via the URL input field. When a search URL is configured on the "Network" tab of Preferences, text entered in the URL field is passed to the search URL as a query parameter. A search query will only occur when Enter is pressed while the [⇒ Search Query] indicator is visible. ### 1.1.2 Links The type and destination of a link are indicated by the link's icon and color: ➤ links to the same domain, and 🌐 to a different domain. The colors are: * Blue for Gemini * Purple for Gopher * Orange for HTTP(S) * Red for other/unknown/invalid (cannot be opened) Link colors remain the same regardless of which color theme is being used for page content. (Color themes are discussed in the Customization section.) When you move the mouse cursor over a link, additional information will appear: the destination domain, with the URL scheme shown for non-Gemini links, and the date of the last visit to the URL. ### 1.1.3 Page caching When navigating to a new page, the old page is cached in memory. If you navigate back, the cached copy of the page is restored. Think of it as rewinding time — you return to a past time as if nothing had happened. The same applies to forward navigation; cached pages are loaded if available. This allows back and forward navigation to happen instantly, without any network requests. The page cache is saved to a file when Lagrange is shut down so it can be restored on the next launch. Maximum size of the cache can be configured on the "Network" tab of Preferences. Note that the entire cache is kept in memory at runtime. When the cache fills up, the oldest and largest page content is removed — both factors contribute to the removal. You can set the maximum size to 0 to disable caching altogether, but that will also disable the page content quick search feature. ### 1.1.4 Opening links in a new tab Holding down ${CTRL} when left-clicking on a link causes it to open in a new tab. Alternatively, middle-clicking a link has the same effect. Right-clicking on a link shows a link-specific context menu. From there, you can also open the link in a new background tab, which will keep the current tab open. ### 1.1.5 Opening links using the keyboard Lagrange has two modes for navigating links with the keyboard. The first uses a modifier key (${ALT} by default): while the modifier is pressed, alphanumeric shortcuts are shown for each visible link in the window. You can then press the corresponding key, still holding the modifier, to open a link. The second mode is based on letters only and focuses on the page row keys. You press and release the activation key ("F" by default) to show the link shortcut letters. In this mode the shortcuts are not in alphabetic order, but instead roughly sorted based on proximity to the F and J keys. You may find this easier to use since it is a simple sequence of key presses without using modifiers, allowing one to keep fingers on or near the home row. You can give it a try now with the link below. Either hold down ${ALT}, or press and release F. => gemini://gemini.circumlunar.space/ Project Gemini ## 1.2 Tabs Press ${CTRL+}T to open a new tab, and ${CTRL+}W to close the current tab. Right-clicking on buttons in the tab bar shows a context menu for additional tab-related functions. The set of open tabs and their full contents are saved when you quit the application and restored when relaunch it. ### 1.2.1 Auto-reloading A tab can be set to auto-reload at given intervals. The setting is remembered until the tab is closed. This is helpful if you keep a periodically updated page open for longer periods of time. The feature can be found in the page context menu: right-click and select "Set Auto-Reload...". ## 1.3 Sidebars 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: * Bookmarks: List of bookmarks that you've created. These appear first in search results for quick and easy access. * Feeds: Entries found on subscribed pages. * 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. * Identities: TLS client certificates. * Outline: List of the headings in the currently open tab. Useful when reading longer documents. ${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. ## 1.4 Bookmarks Press ${CTRL+}D to bookmark the currently open URL. Bookmarks are listed in alphabetic order in the sidebar. There is no support for folders or manual sorting. In addition to a title, bookmarks can have tags. Some tags have a special meaning, but you are free to enter whatever you want in the tags field. In quick search results, tags are given extra weight so they appear higher in results. By default bookmarks are assigned random icons. You can enter a custom icon for a bookmark in the bookmark edit dialog to make it easier to recognize a particular site at a glance. The icon must be a single Unicode character. It will appear in the bookmark list, tab titles, feed entry list, quick search results, and page top banners. ### 1.4.1 Exporting and importing => about:bookmarks The special page "about:bookmarks" is used for exporting bookmarks out of Lagrange. It has three different modes that can be opened via menus: * listing bookmarks alphabetically * listing bookmarks grouped by tag * listing bookmarks as a feed with creation dates The generated page can be saved to Downloads or you can copy all or parts of it to the clipboard. To import all links on a page as bookmarks, use the page context menu to select "Import Links as Bookmarks...". You can also right-click on individual links and select "Bookmark Link...". ### 1.4.2 Remote bookmarks This version of Lagrange does not support automatic bookmark synchronization. By default all bookmarks are stored locally. However, a remote bookmarks feature is provided to allow accessing a set of bookmarks from everywhere. This way you can set up your own centralized bookmark pages or use any public Gemini page as a source of bookmarks. A local bookmark can be turned into a source of remote bookmarks via the Bookmarks context menu. Right-click the bookmark and select "Use as Remote Source". The ⤓ icon is used to indicate remote items in the Bookmarks list. Remote sources are checked when launching Lagrange and manually with the "Refresh Remote Sources" menu item. All links found in the remote sources become remote bookmarks and are included in the Bookmarks list. If a URL is already bookmarked locally, any remote bookmarks with a matching URL are not shown. Note that remote bookmarks are read-only: they cannot be edited or tagged. This means you cannot subscribe to a remote bookmark without first making a local duplicate of it. If you want to make local copies of all the links in the remote source, open the remote source page and use the "Import Links as Bookmarks..." context menu item on it. After refreshing remote sources, the remote copies of the bookmarks will not be shown any more. ### 1.4.3 Special tags * 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. * 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. * "headings" can be used together with "subscribed" to subscribe to new headings instead of Gemini feed links. * The "remotesource" tag marks the bookmark as a source of remote bookmarks. All links on the source pages are shown as remote bookmarks in the Bookmarks list. * The "remote" tag is used for remote bookmarks. These bookmarks cannot be edited or tagged, but you can make a local duplicate to turn it into a regular bookmark. * The "usericon" tag prevents a random icon to be selected for the bookmark. This tag is automatically applied when an icon character is entered in the bookmark editor. ## 1.5 Subscribing to feeds You may be familiar with XML-based RSS and Atom feeds from the web. The Gemini equivalent of these is 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. This makes it very easy to write pages that clients can subscribe to. => gemini://gemini.circumlunar.space/docs/companion/subscription.gmi See "Subscribing to Gemini pages" for more information. Lagrange supports Gemini and Atom feed subscriptions. Atom feeds are automatically translated to the Gemini feed format so they can be viewed and subscribed to like a normal 'text/gemini' page. RSS feeds are not supported. Subscriptions are managed via bookmarks. When you subscribe to a feed page, a bookmark is created and the special "subscribed" tag is applied on it. In the Bookmarks list, this is indicated by a ★ icon. There is no other difference between normal bookmarks and feed subscriptions — you may tag any bookmark as "subscribed" and Lagrange will look through it for feed-style links. The bookmark title is used as the feed title. This defaults to the top heading of the feed index page, but you can edit it to suit your needs. Feeds are refreshed periodically while Lagrange is running, and also immediately after launching if it has been a while since the previous refresh. You may also manually refresh all feeds via the menus or by pressing ${SHIFT+}${CTRL+}R. The Feeds sidebar tab displays recent feed entries. From there you can open entries and mark them as read/unread. To see all entries from all feeds, open the "Feed entries" page. This page also shows how long has it been since the previous feed refresh. => about:feeds ## 1.6 Identities (client certificates) Gemini uses TLS client certificates for user/session identification purposes. Unlike on the web where user identity tracking is covert and automatic, client certificates must be manually taken into use, and you are able to define how long each certificate remains valid. The term "Identity" is used in Lagrange to refer to client certificates. The Identities sidebar tab shows all identities known to Lagrange. Press ${CTRL+}4 to show it. This includes identities created in Lagrange and any identities based on imported X.509 certificates. ### 1.6.1 Creating a new identity Click on the 👤 button in the navigation bar and select "New Identity...". The shortcut for this is ${SHIFT+}${CTRL+}N. Consider any information you enter in the certificate as public — only the Common Name is required to be non-empty. The generated certificate will use the Common Name as the issuer and subject of the certificate, making it clear that the certificate is self-signed. The other required field is the expiration date in "Valid until". Entering a year is sufficient, and means that the certificate is valid until the end of that year. The "Temporary" option prevents the identity from being saved persistently — it will be deleted as soon as you quit Lagrange. Otherwise, the certificate and its private key are written to .crt and .pem files. ### 1.6.2 Using an identity You will need to select an identity when you encounter this error message: > 🔑 Certificate Required Clicking on an identity in the sidebar will toggle it on/off for the currently open URL. On subsequent page loads, the certificate will then be sent to the server when the URL or any URL under it is fetched. You can click on the 👤 button in the navigation bar to see which identity is being used for the current page. As the sidebar is not keyboard-navigable, note that identities can also be accessed via lookup results. Identities matching the search terms are shown as the last category in the lookup results list. From there, one can toggle an identity for the current page, or stop using it on all pages. ### 1.6.3 Importing existing certificates To import an existing X.509 certificate as an identity, press ${SHIFT+}${CTRL+}I or click on 👤 and select "Import..." (macOS: Identity menu → Import...). When the Import Identity dialog opens, it checks the currently open page for any certificates and private keys in PEM format. If found, these are automatically loaded in. You can also press ${CTRL+}V to paste PEM-formatted certificates and/or private keys, or drag-and-drop a PEM-formatted .crt/.key files on the Lagrange window while the dialog is open. Alternatively, you can manually copy the certificate files so Lagrange can find them. At launch, Lagrange looks through its "idents" directory to see if any new certificates have been copied there. (See "Runtime files" below for the location; it is platform-specific) The file format must be PEM. Both a certificate (.crt) and its private key (.key) must be found in "idents" and they must have matching file names. For example: * mycert.crt * mycert.key Lagrange will add a note to the imported identities to mark them as "Imported". ## 1.7 Downloads Press ${CTRL+}S to save the contents of the current page to the Downloads folder. This works on all pages regardless of whether Lagrange can display the contents or not. When image or audio content is displayed inline, you can right-click on it and select "Save to Downloads" in the context menu. The 🔃 button on the right side of the URL input field is the Reload/Stop button — it reloads the current page or stops an ongoing download. During large downloads, an additional progress indicator appears next to the Stop button. The location where downloaded files are saved can be changed in Preferences. The default location is "Downloads" in your home directory. # 2 Customization You can find a number of settings in Preferences to customize the user interface and page contents. ## 2.1 Browsing behavior 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. ## 2.2 Window and UI options Lagrange supports configuring dark and light UI modes separately. There are four different color schemes for the UI: two for dark mode, and two for light mode. These can be selected on the "Window" tab of the Preferences dialog: * Dark mode: "Pure Black" and "Dark" have a fully black or dark gray UI. * Light mode: "Pure White" and "Light" have completety white or light gray UI. On macOS, Lagrange will automatically switch between dark and light modes if the "Use system theme" setting is enabled. On other platforms you'll need to switch manually. On Windows, an option is provided to use a custom window frame. This provides a more consistent visual style for the application. However, the custom frame overrides default behaviors of the window, so if you are a Windows power user expect it to not support all the special interactions. For example, when pressing the Windows+Left/Right key, the window is resized to take over one half of the screen. Normally, Windows would then prompt to select another window to fill the rest of the screen, but this does not happen with the custom window frame. Options for wide window sizes: * Site icon: If there is room on the left side of the page text, the site icon appears there along with the top level heading. This allows one to keep better track of the current reading position in long documents. ## 2.3 Color themes Page content color themes are selected on the "Colors" tab of Preferences. The "Dark theme" is active in dark UI mode, and the "Light theme" is active in light UI mode. * Colorful Dark: All elements of the page are colored; the background is dark and text is light. This mode provides the best visual distinction between different domains because more color combinations are available. * Colorful Light: A single, bright background color with black text. This is the most colorful light theme. Note that colored backgrounds may reduce readability. * Black: Gray body text and brightly colored headings on a black background. * Gray: Slightly brighter version of Black, i.e., gray body text and colored headings on a gray background. * White: Colored headings on a white background. * Sepia: Light sepia background with black text. Does not change depending on domain; use this for readability if you prefer a sepia reading experience. * High Contrast: White background with black text. Does not change depending on domain; use this for readability if you prefer maximum contrast between text and the background. ## 2.4 Page style The "Style" tab of the Preferences dialog lets you customize the appearance and layout of page content. The fonts for headings and other text are selected separately. This way one can achieve a greater number of style variations. Also, headings that are in a different font are more visually distinct and thus easier for one's eyes to scan. There are six fonts available: * "Nunito" is a friendly rounded sans-serif font. * "Fira Sans" is a bolder, narrower, and more angular sans-serif font. * "Literata" is a heavy and serious serif font. * "Tinos" is a lighter serif font. * "Source Sans Pro" is the Lagrange UI font. * "Iosevka" is the monospace font. Other style options: * Line width: This setting determines the maximum width for text lines. "Window" disables the maximum limit and fits text lines to the full width of the window. Shorter lines are generally easier to read. * Quote indicator: Quoted text is shown in an italic font. Additionally, you can select between a quotation mark icon and the traditional vertical line along the left edge of the quote. * Big 1st paragraph: When enabled, the leading paragraph on the page is shown in a slightly larger font. ## 2.5 Proxies Gemini allows relaying requests via a proxy server. On the "Network" tab, you can configure proxy servers for Gemini, Gopher, and HTTP/HTTPS requests. When an HTTP proxy server is configured, HTTP/HTTPS links will no longer open in the system's default web browser but will be loaded via the proxy, expecting it to serve a 'text/gemini' version of the link contents. ## 2.6 Keybindings The "Keys" tab lets you change which keys are bound to UI commands. Click on an item with the mouse and then press the new key combination that you want bound to it. Right-click on a binding to erase it or reset it to the default. Erasing a binding can be useful to disable certain features. For example, if you have no use for keyboard navigation of links via a modifier key, that binding can be erased and use of the modifier key for other purposes will no longer distract you by showing the link shortcuts. To reset all bindings to their defaults, quit Lagrange and delete the "bindings.txt" file in Lagrange's runtime directory. This is a platform-dependent location; see below for more information (under "Runtime files"). # 3 OS integration ## 3.1 Command line options ### URLs URLs on the command line are opened after Lagrange has launched. If more than one URL is given, multiple tabs will be opened. For example: ``` $ lagrange gemini://gus.guru/ ``` ### -E, --echo Debugging utility: internal events are printed to stdout. ### --help Print a list of all the available options. ### --sw Disable hardware accelerated graphics. Note that software rendering is anyway used as a fallback, so usually this option should not be necessary. ### -V, --version Print the application version. ## 3.2 Opening Gemini links On macOS and Windows, Lagrange registers itself as a "gemini:" URL scheme handler, so you can click on Gemini links in any application to open Lagrange with that URL. Likewise (on macOS), .gmi/.gemini file extensions are registered as file formats that Lagrange can view so Finder will know how to open those automatically using Lagrange. ## 3.3 Drop and drop You can drag and drop .gmi files on the Lagrange window to open them in the current tab. Dropping multiple files opens them in separate tabs. This is the recommended way to view local files, because there is no "Open File" menu item. You may also type "file://" URLs in the URL field. ## 3.4 Runtime files Lagrange stores user-specific persistent files in one of the following locations (depending on the operating system): ```User file directories on different operating systems Windows : C:\Users\Name\AppData\Roaming\fi.skyjake.Lagrange macOS : ~/Library/Application Support/fi.skyjake.Lagrange/ Other Unix : ~/.config/lagrange/ ``` ### bindings.txt Each line specifies one keybinding: > {binding-id} {keycode} {flags} * Binding IDs are defined in src/ui/keys.c. * Keycode is one of the SDLK constants from SDL (hexadecimal). * Flags specifies the modifier keys: ```Table of modifier key flags s : Shift a : Alt c : Ctrl g : Gui (Windows/Command/Meta key) k : Caps Lock ``` ### bookmarks.txt Each bookmark is stored as three consecutive lines: > {icon} {creation-timestamp} {url} > {title} > {tags} * The icon is one UTF-32 codepoint as a hexadecimal number, prefixed with zeroes to be always eight bytes long. * Creation time is stored as a UNIX timestamp. * URL, title, and tags are UTF-8 text strings terminated at the newline. ### feeds.txt Cached state of feed subscriptions. The file may be deleted while the application is not running to force a reset of feed contents. Subscriptions themselves are tracked via bookmark tags so deleting the file does not affect which pages are subscribed. The file has three sections. The first one is simply a UNIX timestamp indicating when feeds were last refreshed. The second section begins after the comment: ``` # Feeds ``` In this section, each line identifies a subscription so it can be referenced in the feed entries: > {feed-id} {feed-url} * The feed ID a hexadecimal number and always eight characters long. The third section contains all the discovered feed entries. It begins after the comment: ``` # Entries ``` Each feed entry consists of five consecutive lines: > {feed-id} > {posted-time} > {discovery-time} > {url} > {title} * The feed ID is one of the identifiers from the Feeds section (without zeroes for padding). * The posted time is a UNIX timestamp that specifies when the entry was posted. This is determined from the "YYYY-MM-DD" date found on the link line. If this is a "new headings" subscription, the posted time and the discovery time are the same. * The discovery time is a UNIX timestamp that marks when the entry was added to the cache. ### idents.lgr and idents/ idents.lgr is a binary file that contains metadata about the client certificates known to the application, for example on which URLs a particular certificate should be active. The "idents" subdirectory is where the actual client certificates are stored. Each certificate comprises a .crt and a .key file in PEM format. Certificates generated inside the application use the certificate fingerprint as the file name. ### modmap.txt Translation table for keyboard modifiers. For example, one could swap the Alt and Ctrl keys by changing their mappings. See the comment in the file that explains the format and usage. ### mimehooks.txt See section 4. ### prefs.cfg Persistent configuration variables. The file is rewritten when the application is closed. Each line is interpreted as an internal UI event like those printed with the --echo command line option. ### state.lgr A binary file that contains the current state of the application: open tabs and their scroll positions, and navigation history with cached page content (up to the configured cache limit). You may delete this file when the application is not running to close all tabs and clear the page content cache. ### trusted.txt Trusted server certificates. Each line specifies the certificate fingerprint of one domain: > {domain} {valid-until} {fingerprint} * Domain names are stored in punycode format if they contain non-ASCII characters. * The "valid until" expiration date is a UNIX timestamp. ### visited.2.txt The combined navigation history of all tabs. Each line specifies one URL: > {last-visited} {flags} {url} * The last visited time is stored as a UNIX timestamp. * Flags is always four bytes long: ``` 0000 : No flags set 0001 : Transient ``` The Transient flag is used to indicate redirections and for marking feed entries as read without actually visiting the URL. URLs with the Transient flag do not appear in the History sidebar tab. ## 3.5 Environment variables ### LAGRANGE_OVERRIDE_DPI Override display DPI detection with a user-provided value. The same value is applied to all displays. This is useful if your display's DPI value is not being detected correctly. Example: ```96 is the default DPI value LAGRANGE_OVERRIDE_DPI=96 ``` # 4 MIME hooks MIME hooks enable piping Gemini responses through external programs for arbitrary processing. This allows leveraging scripting languages and binary executables to perform format conversions, content transformation, and data analysis. Hooks are configured using the file "mimehooks.txt" in Lagrange's config directory. Each hook has a regexp that is matched against the response MIME type and parameters, and each matching hook is offered the response body via stdin. The called external programs are free to rewrite the entire response, including the MIME type. If one of the hooks returns a valid response, it is used as the final response of the Gemini request. Example use cases: * Parsing an Atom XML feed and generating a Gemini feed index page * Rendering a MIDI file using Timidity as PCM WAV * Generate a graph PNG based on values in a CSV * Converting HTML or Markdown to 'text/gemini' * Language translations ## 4.1 Interface 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. The MIME type and parameters are split at semicolons, so "text/gemini; lang=ja" would be called as: ``` hookprogram text/gemini lang=ja ``` Output from the program (via stdout) must be a valid "20" Gemini response that includes the new MIME type: ``` 20 text/gemini; lang=en\r\n {...body...} ``` Any output that does not follow this format is considered to mean that the hook refused to process the contents. The next hook will be offered the response instead. ## 4.2 mimehooks.txt syntax Like other Lagrange configuration lines, mimehooks.txt has a simple line-oriented syntax. Lagrange must be restarted for changes to the configuration file to take effect. Each hook is specified as three lines: * A human-readable label (for reporting to the user) * MIME type/parameter regular expression * Command to execute, plus additional arguments each separated with semicolons For example: ``` Convert Atom to Gemini feed application/xml /usr/bin/python3;/home/jaakko/atomconv.py ``` The hook program is executed directly without involving the shell. This means scripts must be invoked via the interpreter executable. ## 4.3 Example: Converting from Atom to Gemini The following simple Python script demonstrates how a MIME hook could be used to parse an Atom XML document using Python 3 and output a Gemini feed index page based on the parsed entries. This is just a simple example; a more robust script could include more content from the Atom feed and handle errors, too. ``` import sys import xml.etree.ElementTree as ET def atomtag(n): return '{http://www.w3.org/2005/Atom}' + n root = ET.fromstring(sys.stdin.read()) if root.tag != atomtag('feed'): sys.exit(0) feed_title = '' feed_author = '' feed_entries = [] for child in root: if child.tag == atomtag('title'): feed_title = child.text elif child.tag == atomtag('entry'): feed_entries.append(child) print("20 text/gemini\r") print(f'# {feed_title}') for entry in feed_entries: entry_date = '' entry_title = '' entry_link = '' for child in entry: if child.tag == atomtag('updated'): entry_date = child.text[:10] elif child.tag == atomtag('title'): entry_title = child.text elif child.tag == atomtag('link'): entry_link = child.attrib['href'] print(f'=> {entry_link} {entry_date} {entry_title}') ``` # Open source licenses => about:license