summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------lib/the_Foundation0
-rw-r--r--po/fr.po233
-rw-r--r--po/ru.po56
-rw-r--r--po/sk.po285
-rw-r--r--res/about/version.gmi6
-rw-r--r--res/fi.skyjake.Lagrange.appdata.xml34
-rw-r--r--res/lang/fr.binbin25184 -> 25862 bytes
-rw-r--r--res/lang/ru.binbin36350 -> 37144 bytes
-rw-r--r--src/app.c1
-rw-r--r--src/gmcerts.h10
-rw-r--r--src/macos.m1
-rw-r--r--src/ui/documentwidget.c111
-rw-r--r--src/ui/inputwidget.c28
-rw-r--r--src/ui/labelwidget.c15
-rw-r--r--src/ui/paint.c12
-rw-r--r--src/ui/visbuf.c8
-rw-r--r--src/ui/window.c7
-rw-r--r--src/ui/window.h2
18 files changed, 723 insertions, 86 deletions
diff --git a/lib/the_Foundation b/lib/the_Foundation
Subproject 33c52862c69c964f8eef025841b4f32072ed597 Subproject 4a2cd8896bf7fabbe7345c245a052083c6c1c3e
diff --git a/po/fr.po b/po/fr.po
index e0522323..e0d5a5b2 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -1,7 +1,7 @@
1msgid "" 1msgid ""
2msgstr "" 2msgstr ""
3"Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" 3"Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n"
4"PO-Revision-Date: 2021-05-21 08:18+0000\n" 4"PO-Revision-Date: 2021-08-05 14:07+0000\n"
5"Last-Translator: MCMic <come@chilliet.eu>\n" 5"Last-Translator: MCMic <come@chilliet.eu>\n"
6"Language-Team: French <http://weblate.skyjake.fi/projects/lagrange/ui/fr/>\n" 6"Language-Team: French <http://weblate.skyjake.fi/projects/lagrange/ui/fr/>\n"
7"Language: fr\n" 7"Language: fr\n"
@@ -583,7 +583,7 @@ msgid "feeds.refresh"
583msgstr "Actualiser les flux" 583msgstr "Actualiser les flux"
584 584
585msgid "hint.findtext" 585msgid "hint.findtext"
586msgstr "Chercher dans le texte de la page" 586msgstr "chercher dans le texte de la page"
587 587
588msgid "mb.per.sec" 588msgid "mb.per.sec"
589msgstr "Mo/s" 589msgstr "Mo/s"
@@ -730,7 +730,7 @@ msgid "heading.prefs.colors"
730msgstr "Couleurs" 730msgstr "Couleurs"
731 731
732msgid "heading.prefs.fonts" 732msgid "heading.prefs.fonts"
733msgstr "POLICES" 733msgstr "Polices"
734 734
735msgid "prefs.doctheme.name.white" 735msgid "prefs.doctheme.name.white"
736msgstr "Blanc" 736msgstr "Blanc"
@@ -1437,7 +1437,7 @@ msgid "prefs.mono"
1437msgstr "Chasse fixe :" 1437msgstr "Chasse fixe :"
1438 1438
1439msgid "hint.newident.date" 1439msgid "hint.newident.date"
1440msgstr "AAAA-MM-JJ HH:MM:SS" 1440msgstr "AAAA ou AAAA-MM-JJ"
1441 1441
1442msgid "link.hint.audio" 1442msgid "link.hint.audio"
1443msgstr "Lire l’audio" 1443msgstr "Lire l’audio"
@@ -1604,3 +1604,228 @@ msgstr "Échanger les côtés"
1604 1604
1605msgid "keys.split.menu" 1605msgid "keys.split.menu"
1606msgstr "Définir le mode de vues séparées" 1606msgstr "Définir le mode de vues séparées"
1607
1608msgid "bookmark.export.title.folder"
1609msgstr "Signets"
1610
1611msgid "bookmark.export.title.tag"
1612msgstr "Étiquettes de signet"
1613
1614msgid "bookmark.export.saving"
1615msgstr ""
1616"Enregistrez cette page pour les exporter, ou copiez-les dans le presse-"
1617"papiers."
1618
1619msgid "bookmark.export.taginfo"
1620msgstr ""
1621"Dans cette liste, chaque titre représente une étiquette de signet. Seuls les "
1622"signets étiquetés sont listés. Ceux avec plusieurs étiquettes sont dupliqués "
1623"sous chacune d'elles."
1624
1625msgid "hint.upload.text"
1626msgstr "entrer le texte à téléverser"
1627
1628msgid "upload.mime"
1629msgstr "Type MIME :"
1630
1631msgid "error.certverify.msg"
1632msgstr ""
1633"La connexion au serveur a été interrompue car le certificat TLS reçu ne "
1634"correspond pas à celui auquel nous faisons confiance. Veuillez vérifier si "
1635"le serveur a signalé un changement de certificat. Si ce n’est pas le cas, il "
1636"est possible qu’un tiers se fasse passer pour le serveur que vous essayez "
1637"d’atteindre.\n"
1638"\n"
1639"Le certificat peut être marqué comme étant de confiance dans les "
1640"Informations de la Page."
1641
1642msgid "dlg.input.linebreak"
1643msgstr "Saut de ligne"
1644
1645msgid "link.file.delete"
1646msgstr "Supprimer le fichier"
1647
1648msgid "heading.file.delete"
1649msgstr "SUPPRESSION DE FICHIER"
1650
1651msgid "dlg.file.delete.confirm"
1652msgstr "Voulez-vous vraiment supprimer ce fichier ?"
1653
1654msgid "dlg.file.delete"
1655msgstr "Supprimer"
1656
1657msgid "prefs.animate"
1658msgstr "Animations :"
1659
1660msgid "upload.port"
1661msgstr "Port…"
1662
1663msgid "heading.uploadport"
1664msgstr "PORT DE TÉLÉVERSEMENT TITAN"
1665
1666msgid "dlg.uploadport.msg"
1667msgstr ""
1668"Définit le port du serveur Titan à utiliser pour cette URL.\n"
1669"Le port est enregistré dans la configuration spécifique au site."
1670
1671msgid "dlg.uploadport.set"
1672msgstr "Définir le port"
1673
1674msgid "heading.prefs.uitheme"
1675msgstr "INTERFACE"
1676
1677msgid "prefs.userfont"
1678msgstr "Police des symboles :"
1679
1680msgid "keys.split.item"
1681msgstr "Menu Diviser la vue :"
1682
1683msgid "error.certexpired"
1684msgstr "Certificat expiré"
1685
1686msgid "error.certexpired.msg"
1687msgstr "La connexion au serveur a été annulée car son certificat TLS a expiré."
1688
1689msgid "prefs.pinsplit.left"
1690msgstr "Côté gauche"
1691
1692msgid "prefs.pinsplit.right"
1693msgstr "Coté droit"
1694
1695msgid "hint.prefs.userfont"
1696msgstr "chemin d’une police TrueType"
1697
1698msgid "prefs.linespacing"
1699msgstr "Interligne :"
1700
1701msgid "prefs.memorysize"
1702msgstr "Taille mémoire :"
1703
1704msgid "close"
1705msgstr "Fermer"
1706
1707msgid "ident.export"
1708msgstr "Exporter"
1709
1710msgid "error.server.msg"
1711msgstr "Le serveur a répondu avec le message :"
1712
1713msgid "dlg.newident.scope"
1714msgstr "Utiliser pour :"
1715
1716msgid "dlg.newident.more"
1717msgstr "Plus…"
1718
1719msgid "dlg.newident.scope.domain"
1720msgstr "Domaine actuel"
1721
1722msgid "dlg.newident.scope.page"
1723msgstr "Page actuelle"
1724
1725msgid "dlg.newident.scope.none"
1726msgstr "Non utilisée"
1727
1728msgid "prefs.scrollspeed.keyboard"
1729msgstr "Vitesse du clavier :"
1730
1731msgid "prefs.scrollspeed.mouse"
1732msgstr "Vitesse de la souris :"
1733
1734msgid "prefs.returnkey"
1735msgstr "Touche Entrée :"
1736
1737msgid "prefs.returnkey.linebreak"
1738msgstr "Saut de ligne"
1739
1740msgid "prefs.returnkey.accept"
1741msgstr "Accepter"
1742
1743msgid "menu.save.downloads.open"
1744msgstr "Enregistrer dans les téléchargements et ouvrir le fichier"
1745
1746msgid "menu.pageinfo"
1747msgstr "Afficher les informations de la page"
1748
1749msgid "status.query.tight"
1750msgstr "Recherche"
1751
1752msgid "media.untitled.image"
1753msgstr "Image"
1754
1755msgid "media.untitled.audio"
1756msgstr "Audio"
1757
1758msgid "feeds.atom.translated"
1759msgstr ""
1760"Ce document XML Atom a été automatiquement converti en flux Gemini pour qu’"
1761"il soit possible de s’y abonner."
1762
1763msgid "menu.openfile"
1764msgstr "Ouvrir un fichier…"
1765
1766msgid "bookmark.export.title.time"
1767msgstr "Signets créés"
1768
1769msgid "bookmark.export.count"
1770msgid_plural "bookmark.export.count.n"
1771msgstr[0] "Vous avez %d signet."
1772msgstr[1] "Vous avez %d signets."
1773
1774msgid "bookmark.export.format.sub"
1775msgstr ""
1776"Cette page est formatée selon la spécification complémentaire « Subscribing "
1777"to Gemini pages »."
1778
1779msgid "bookmark.export.format.linklines"
1780msgstr "Chaque lien représente un signet."
1781
1782msgid "bookmark.export.format.otherlines"
1783msgstr ""
1784"Les lignes avec puce et les citations sont réservées pour des informations "
1785"supplémentaires sur le signet qui les précède. Les lignes de texte et le "
1786"texte préformaté sont traités comme des commentaires et doivent être ignorés."
1787
1788msgid "bookmark.export.format.folders"
1789msgstr "La structure du dossier est définie par les titres de niveaux 2 et 3."
1790
1791msgid "bookmark.export.format.tags"
1792msgstr "Les étiquettes sont définies par les titres de niveau 2."
1793
1794msgid "menu.page.upload"
1795msgstr "Téléverser la page avec Titan"
1796
1797msgid "heading.upload"
1798msgstr "TÉLÉVERSEMENT AVEC TITAN"
1799
1800msgid "heading.upload.text"
1801msgstr "Texte"
1802
1803msgid "heading.upload.file"
1804msgstr "Fichier"
1805
1806msgid "upload.file.name"
1807msgstr "Nom du fichier :"
1808
1809msgid "upload.file.drophere"
1810msgstr "(glisser-et-déposer un fichier sur la fenêtre)"
1811
1812msgid "upload.file.size"
1813msgstr "Taille du fichier :"
1814
1815msgid "upload.token"
1816msgstr "Jeton de téléversement :"
1817
1818msgid "hint.upload.token"
1819msgstr "voir les instructions du serveur"
1820
1821msgid "dlg.upload.send"
1822msgstr "Téléverser"
1823
1824msgid "keys.upload"
1825msgstr "Téléverser la page avec Titan"
1826
1827msgid "keys.split.next"
1828msgstr "Basculer entre les côtés"
1829
1830msgid "error.certverify"
1831msgstr "Certificat inconnu"
diff --git a/po/ru.po b/po/ru.po
index e6d76237..7955c82a 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -3,7 +3,7 @@ msgstr ""
3"Project-Id-Version: PACKAGE VERSION\n" 3"Project-Id-Version: PACKAGE VERSION\n"
4"Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n" 4"Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n"
5"POT-Creation-Date: 2021-03-23 19:02+0000\n" 5"POT-Creation-Date: 2021-03-23 19:02+0000\n"
6"PO-Revision-Date: 2021-07-25 18:06+0000\n" 6"PO-Revision-Date: 2021-08-10 12:07+0000\n"
7"Last-Translator: Olga Smirnova <mistresssilvara@hotmail.com>\n" 7"Last-Translator: Olga Smirnova <mistresssilvara@hotmail.com>\n"
8"Language-Team: Russian <http://weblate.skyjake.fi/projects/lagrange/ui/ru/>\n" 8"Language-Team: Russian <http://weblate.skyjake.fi/projects/lagrange/ui/ru/>\n"
9"Language: ru\n" 9"Language: ru\n"
@@ -1760,3 +1760,57 @@ msgstr ""
1760"выполнялось подключение.\n" 1760"выполнялось подключение.\n"
1761"\n" 1761"\n"
1762"Сертификат можно пометить как доверенный в окне «Сведения о странице»." 1762"Сертификат можно пометить как доверенный в окне «Сведения о странице»."
1763
1764msgid "media.untitled.image"
1765msgstr "Изображение"
1766
1767msgid "feeds.atom.translated"
1768msgstr ""
1769"Этот XML-документ Atom был преобразован в ленту Gemini, чтобы на нее можно "
1770"было подписаться."
1771
1772msgid "bookmark.export.count"
1773msgid_plural "bookmark.export.count.n"
1774msgstr[0] "Всего %d закладка."
1775msgstr[1] "Всего %d закладки."
1776msgstr[2] "Всего %d закладок."
1777
1778msgid "bookmark.export.format.sub"
1779msgstr ""
1780"Эта страница отформатирована в соответствии со вспомогательной спецификацией "
1781"«Subscribing to Gemini pages»."
1782
1783msgid "bookmark.export.format.otherlines"
1784msgstr ""
1785"Маркеры списка и цитаты содержат дополнительные сведения о предыдущей "
1786"закладке. Строки текста и форматированный текст считаются примечаниями и "
1787"игнорируются."
1788
1789msgid "bookmark.export.taginfo"
1790msgstr ""
1791"Каждый заголовок в этом списке представляет тэг закладки. Включены только "
1792"закладки с тэгами. Закладки с несколькими тэгами появляются несколько раз."
1793
1794msgid "media.untitled.audio"
1795msgstr "Аудио"
1796
1797msgid "bookmark.export.title.folder"
1798msgstr "Закладки"
1799
1800msgid "bookmark.export.title.time"
1801msgstr "Созданные закладки"
1802
1803msgid "bookmark.export.saving"
1804msgstr "Сохраните страницу для экспорта, или скопируйте их в Буфер обмена."
1805
1806msgid "bookmark.export.title.tag"
1807msgstr "Тэги закладок"
1808
1809msgid "bookmark.export.format.linklines"
1810msgstr "Каждая ссылка представляет собой закладку."
1811
1812msgid "bookmark.export.format.folders"
1813msgstr "Структура папок выражается заголовками 2/3 уровня."
1814
1815msgid "bookmark.export.format.tags"
1816msgstr "Тэги представлены заголовками 2-го уровня."
diff --git a/po/sk.po b/po/sk.po
new file mode 100644
index 00000000..f622e5ef
--- /dev/null
+++ b/po/sk.po
@@ -0,0 +1,285 @@
1msgid ""
2msgstr ""
3"Report-Msgid-Bugs-To: jaakko.keranen@iki.fi\n"
4"PO-Revision-Date: 2021-08-12 08:52+0000\n"
5"Last-Translator: Marek Ľach <mareklachbc@tutanota.com>\n"
6"Language-Team: Slovak <http://weblate.skyjake.fi/projects/lagrange/ui/sk/>\n"
7"Language: sk\n"
8"MIME-Version: 1.0\n"
9"Content-Type: text/plain; charset=UTF-8\n"
10"Content-Transfer-Encoding: 8bit\n"
11"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
12"X-Generator: Weblate 4.5.1\n"
13
14# Link download progress message.
15msgid "doc.fetching"
16msgstr "Získava sa"
17
18msgid "doc.archive.view"
19msgstr "Zobraziť obsah archívu"
20
21msgid "media.untitled.image"
22msgstr "Obrázok"
23
24msgid "media.untitled.audio"
25msgstr "Zvuk"
26
27# Inline download status message.
28msgid "media.download.complete"
29msgstr "Sťahovanie dokončené."
30
31# Used in inline audio player metadata popup.
32msgid "audio.meta.title"
33msgstr "Názov"
34
35# Used in inline audio player metadata popup.
36msgid "audio.meta.genre"
37msgstr "Žáner"
38
39# Used in inline audio player metadata popup.
40msgid "audio.meta.date"
41msgstr "Dátum"
42
43# Used in about:feeds.
44msgid "feeds.list.title"
45msgstr "Položky kanála"
46
47#, c-format
48msgid "feeds.list.entrycount"
49msgid_plural "feeds.list.entrycount.n"
50msgstr[0] "celkovo %z položka"
51msgstr[1] "celkovo %z položiek"
52msgstr[2] "celkovo %z položky"
53
54msgid "feeds.list.refreshtime.now"
55msgstr "Posledné obnovenie prebehlo iba pred chvíľou."
56
57#, c-format
58msgid "feeds.list.refreshtime"
59msgstr "Posledné obnovenie prebehlo pred %s."
60
61#, c-format
62msgid "minutes.ago"
63msgid_plural "minutes.ago.n"
64msgstr[0] "pred %d minútou"
65msgstr[1] "pred %d minútami"
66msgstr[2] "pred %d minútami"
67
68#, c-format
69msgid "days.ago"
70msgid_plural "days.ago.n"
71msgstr[0] "pred %d dňom"
72msgstr[1] "pred %d dňami"
73msgstr[2] "pred %d dňami"
74
75msgid "about.tagline"
76msgstr "Pekný Gemini klient"
77
78msgid "about.version"
79msgstr "Verzia"
80
81msgid "cancel"
82msgstr "Zrušiť"
83
84msgid "close"
85msgstr "Zatvoriť"
86
87msgid "dlg.message.ok"
88msgstr "Pokračovať"
89
90msgid "toggle.no"
91msgstr "NIE"
92
93msgid "menu.title.file"
94msgstr "Súbor"
95
96msgid "menu.title.edit"
97msgstr "Upraviť"
98
99msgid "menu.title.view"
100msgstr "Zobrazenie"
101
102msgid "menu.title.bookmarks"
103msgstr "Záložky"
104
105msgid "menu.title.identity"
106msgstr "Totožnosť"
107
108msgid "menu.title.help"
109msgstr "Pomoc"
110
111msgid "menu.newtab"
112msgstr "Nová karta"
113
114msgid "menu.closetab"
115msgstr "Zatvoriť kartu"
116
117msgid "menu.closetab.right"
118msgstr "Zatvoriť karty smerom doprava"
119
120msgid "menu.split.merge"
121msgstr "Spojiť karty"
122
123msgid "menu.split.swap"
124msgstr "Prehodiť strany"
125
126msgid "menu.split.horizontal"
127msgstr "Horizontálne"
128
129msgid "menu.openlocation"
130msgstr "Otvoriť lokalitu…"
131
132msgid "menu.downloads"
133msgstr "Ukázať stiahnuté súbory"
134
135msgid "menu.pageinfo"
136msgstr "Ukázať informácie o stránke"
137
138msgid "macos.menu.find"
139msgstr "Nájsť"
140
141# Used on desktop operating systems. "Downloads" refers to the user's configured downloads directory.
142msgid "menu.save.downloads"
143msgstr "Uložiť medzi stiahnuté súbory"
144
145msgid "menu.save.downloads.open"
146msgstr "Uložiť medzi stiahnuté, a otvoriť súbor"
147
148msgid "menu.zoom.in"
149msgstr "Priblížiť"
150
151msgid "menu.zoom.out"
152msgstr "Oddialiť"
153
154msgid "menu.view.split"
155msgstr "Rozdelené zobrazenie..."
156
157msgid "macos.menu.bookmarks.list"
158msgstr "Ukázať všetky"
159
160msgid "menu.bookmarks.bytag"
161msgstr "Ukázať záložky podľa značenia"
162
163msgid "menu.bookmarks.bytime"
164msgstr "Zobraziť záložky podľa dátumu vytvorenia"
165
166msgid "macos.menu.bookmarks.bytime"
167msgstr "Zobraziť podľa dátumu vytvorenia"
168
169msgid "menu.feeds.entrylist"
170msgstr "Ukázať položky kanála"
171
172msgid "menu.preferences"
173msgstr "Voľby…"
174
175msgid "menu.help"
176msgstr "Pomoc"
177
178msgid "menu.copy"
179msgstr "Kopírovať"
180
181msgid "menu.paste"
182msgstr "Vložiť"
183
184msgid "menu.select.clear"
185msgstr "Zrušiť výber"
186
187# Used in the View menu on macOS. Shows sidebar and switches sidebar tab.
188msgid "menu.show.bookmarks"
189msgstr "Ukázať záložky"
190
191# Used in the View menu on macOS. Shows sidebar and switches sidebar tab.
192msgid "menu.show.feeds"
193msgstr "Ukázať kanály"
194
195# Used in the View menu on macOS. Shows sidebar and switches sidebar tab.
196msgid "menu.show.history"
197msgstr "Ukázať históriu"
198
199msgid "menu.back"
200msgstr "Ísť späť"
201
202msgid "menu.forward"
203msgstr "Ísť vpred"
204
205msgid "menu.root"
206msgstr "Ísť na základný"
207
208msgid "menu.parent"
209msgstr "Ísť na nadradený"
210
211msgid "menu.reload"
212msgstr "Načítať stránku znova"
213
214msgid "menu.page.subscribe"
215msgstr "Odoberať stránku…"
216
217msgid "menu.about"
218msgstr "O"
219
220msgid "panel.back"
221msgstr "Späť"
222
223msgid "doc.pre.nocaption"
224msgstr "Predformátovaný text bez popisu"
225
226# Inline download status message.
227msgid "media.download.warnclose"
228msgstr "Sťahovanie bude zrušené v prípade, že je táto záložka zatvorená."
229
230# Used in inline audio player metadata popup.
231msgid "audio.meta.artist"
232msgstr "Tvorca"
233
234#, c-format
235msgid "feeds.list.counts"
236msgid_plural "feeds.list.counts.n"
237msgstr[0] "Odoberáte %z kanál, ktorý obsahuje %%.\n"
238msgstr[1] "Odoberáte %z kanálov, ktoré obsahujú %%.\n"
239msgstr[2] "Odoberáte %z kanály, ktoré obsahujú %%.\n"
240
241#, c-format
242msgid "hours.ago"
243msgid_plural "hours.ago.n"
244msgstr[0] "pred %d hodinou"
245msgstr[1] "pred %d hodinami"
246msgstr[2] "pred %d hodinami"
247
248msgid "about.powered"
249msgstr "Poháňané SDL 2, OpenSSL, a ☕️"
250
251msgid "toggle.yes"
252msgstr "ÁNO"
253
254msgid "menu.closetab.other"
255msgstr "Zatvoriť ostatné karty"
256
257msgid "menu.closetab.left"
258msgstr "Zatvoriť karty smerom doľava"
259
260msgid "menu.split.vertical"
261msgstr "Vertikálne"
262
263msgid "menu.find"
264msgstr "Nájsť na stránke"
265
266# Used on iOS. "Files" refers to Apple's iOS app where you can pick an iCloud folder.
267msgid "menu.save.files"
268msgstr "Uložiť do Súborov"
269
270msgid "menu.bookmarks.list"
271msgstr "Ukázať všetky záložky"
272
273msgid "macos.menu.bookmarks.bytag"
274msgstr "Ukázať podľa značenia"
275
276msgid "menu.cut"
277msgstr "Vystrihnúť"
278
279# Used in the Edit menu on macOS. Note: could be replaced with menu.page.copyurl.
280msgid "menu.copy.pagelink"
281msgstr "Kopírovať adresu stránky"
282
283# Used in the View menu on macOS. Shows sidebar and switches sidebar tab.
284msgid "menu.show.identities"
285msgstr "Ukázať totožnosti"
diff --git a/res/about/version.gmi b/res/about/version.gmi
index 72b46e23..1fce4188 100644
--- a/res/about/version.gmi
+++ b/res/about/version.gmi
@@ -9,8 +9,14 @@
9## 1.6.3 9## 1.6.3
10* Select all text in an input field using Shift+Ctrl+A (macOS: ⌘A). 10* Select all text in an input field using Shift+Ctrl+A (macOS: ⌘A).
11* Input fields do not lose focus when the window becomes inactive, making it easier to resume input afterwards. 11* Input fields do not lose focus when the window becomes inactive, making it easier to resume input afterwards.
12* Fixed delay after switching to split view mode.
13* Fixed what gets drawn in an empty tab, before a document is available for rendering (e.g., after switching to split view mode).
14* Fixed highlighting the domain name in URL input fields.
15* Fixed hiding the Gemini URL scheme in input fields when the window is narrow.
12* Fixed the line break key modifier inadvertently affecting URL input fields, where line breaks are not allowed. 16* Fixed the line break key modifier inadvertently affecting URL input fields, where line breaks are not allowed.
13* Fixed the line break key modifier affecting the upload dialog's text field. 17* Fixed the line break key modifier affecting the upload dialog's text field.
18* Fixed a potential hang when closing a socket before the connection is fully opened.
19* Updated translations.
14 20
15## 1.6.2 21## 1.6.2
16* Added `--tab-url` to print currently active tab's URL. 22* Added `--tab-url` to print currently active tab's URL.
diff --git a/res/fi.skyjake.Lagrange.appdata.xml b/res/fi.skyjake.Lagrange.appdata.xml
index 1db2717c..0259aa1e 100644
--- a/res/fi.skyjake.Lagrange.appdata.xml
+++ b/res/fi.skyjake.Lagrange.appdata.xml
@@ -45,6 +45,40 @@
45 <update_contact>jaakko.keranen@iki.fi</update_contact> 45 <update_contact>jaakko.keranen@iki.fi</update_contact>
46 46
47 <releases> 47 <releases>
48 <release version="1.6.3" date="2021-08-15">
49 <description>
50 <p>Version 1.6 adds support for bidirectional text and complex scripts,
51 right-to-left paragraph layout, uploads using the Titan protocol,
52 and has an improved mechanism for tracking trust in server
53 certificates. Page contents can be fully cached in memory for more
54 efficient backward navigation. There are also UI improvements like
55 a reorganized Preferences and a setting for smooth scrolling
56 speed.</p>
57 <p>Changes and fixes in v1.6.3:</p>
58 <ul>
59 <li>Select all text in an input field using Shift+Ctrl+A.</li>
60 <li>Input fields do not lose focus when the window becomes
61 inactive, making it easier to resume input afterwards.</li>
62 <li>Fixed delay after switching to split view mode.</li>
63 <li>Fixed what gets drawn in an empty tab, before a document is
64 available for rendering (e.g., after switching to split view
65 mode).</li>
66 <li>Fixed highlighting the domain name in URL input fields.</li>
67 <li>Fixed hiding the Gemini URL scheme in input fields when the
68 window is narrow.</li>
69 <li>Fixed the line break key modifier inadvertently affecting URL
70 input fields, where line breaks are not allowed.</li>
71 <li>Fixed the line break key modifier affecting the upload dialog's
72 text field.</li>
73 <li>Fixed a potential hang when closing a socket before the
74 connection is fully opened.</li>
75 <li>Updated translations.</li>
76 </ul>
77 <p>The full release notes can be viewed inside the app by opening
78 the "about:version" page.</p>
79 </description>
80 <url>https://github.com/skyjake/lagrange/releases/tag/v1.6.3</url>
81 </release>
48 <release version="1.6.2" date="2021-08-03"> 82 <release version="1.6.2" date="2021-08-03">
49 <description> 83 <description>
50 <p>Version 1.6 adds support for bidirectional text and complex scripts, 84 <p>Version 1.6 adds support for bidirectional text and complex scripts,
diff --git a/res/lang/fr.bin b/res/lang/fr.bin
index 9c3024f8..86282bc2 100644
--- a/res/lang/fr.bin
+++ b/res/lang/fr.bin
Binary files differ
diff --git a/res/lang/ru.bin b/res/lang/ru.bin
index 176fa4fe..687adc01 100644
--- a/res/lang/ru.bin
+++ b/res/lang/ru.bin
Binary files differ
diff --git a/src/app.c b/src/app.c
index 8318eee4..bcd06722 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1992,6 +1992,7 @@ iBool handleCommand_App(const char *cmd) {
1992 (argLabel_Command(cmd, "axis") ? vertical_WindowSplit : 0) | (arg_Command(cmd) << 1); 1992 (argLabel_Command(cmd, "axis") ? vertical_WindowSplit : 0) | (arg_Command(cmd) << 1);
1993 const char *url = suffixPtr_Command(cmd, "url"); 1993 const char *url = suffixPtr_Command(cmd, "url");
1994 setCStr_String(get_Window()->pendingSplitUrl, url ? url : ""); 1994 setCStr_String(get_Window()->pendingSplitUrl, url ? url : "");
1995 postRefresh_App();
1995 return iTrue; 1996 return iTrue;
1996 } 1997 }
1997 else if (equal_Command(cmd, "window.retain")) { 1998 else if (equal_Command(cmd, "window.retain")) {
diff --git a/src/gmcerts.h b/src/gmcerts.h
index 1a9480f7..02a41c14 100644
--- a/src/gmcerts.h
+++ b/src/gmcerts.h
@@ -60,9 +60,10 @@ iDeclareTypeConstructionArgs(GmCerts, const char *saveDir)
60 60
61typedef iBool (*iGmCertsIdentityFilterFunc)(void *context, const iGmIdentity *); 61typedef iBool (*iGmCertsIdentityFilterFunc)(void *context, const iGmIdentity *);
62 62
63iBool checkTrust_GmCerts (iGmCerts *, iRangecc domain, uint16_t port, const iTlsCertificate *cert); 63iBool checkTrust_GmCerts (iGmCerts *, iRangecc domain, uint16_t port,
64void setTrusted_GmCerts (iGmCerts *, iRangecc domain, uint16_t port, const iBlock *fingerprint, 64 const iTlsCertificate *cert);
65 const iDate *validUntil); 65void setTrusted_GmCerts (iGmCerts *, iRangecc domain, uint16_t port,
66 const iBlock *fingerprint, const iDate *validUntil);
66iTime domainValidUntil_GmCerts(const iGmCerts *, iRangecc domain, uint16_t port); 67iTime domainValidUntil_GmCerts(const iGmCerts *, iRangecc domain, uint16_t port);
67 68
68/** 69/**
@@ -81,7 +82,8 @@ iGmIdentity * newIdentity_GmCerts (iGmCerts *, int flags, iDate validU
81 const iString *userId, const iString *domain, 82 const iString *userId, const iString *domain,
82 const iString *org, const iString *country); 83 const iString *org, const iString *country);
83 84
84void importIdentity_GmCerts (iGmCerts *, iTlsCertificate *cert, const iString *notes); /* takes ownership */ 85void importIdentity_GmCerts (iGmCerts *, iTlsCertificate *cert,
86 const iString *notes); /* takes ownership */
85void deleteIdentity_GmCerts (iGmCerts *, iGmIdentity *identity); 87void deleteIdentity_GmCerts (iGmCerts *, iGmIdentity *identity);
86void saveIdentities_GmCerts (const iGmCerts *); 88void saveIdentities_GmCerts (const iGmCerts *);
87 89
diff --git a/src/macos.m b/src/macos.m
index 6bec3c12..d588fa4a 100644
--- a/src/macos.m
+++ b/src/macos.m
@@ -60,6 +60,7 @@ static NSString *currentSystemAppearance_(void) {
60} 60}
61 61
62iBool shouldDefaultToMetalRenderer_MacOS(void) { 62iBool shouldDefaultToMetalRenderer_MacOS(void) {
63 /* TODO: Test if SDL 2.0.16 works better (no stutters with Metal?). */
63 return iFalse; /* 64 return iFalse; /*
64 const iInt2 ver = macVer_(); 65 const iInt2 ver = macVer_();
65 return ver.x > 10 || ver.y > 13;*/ 66 return ver.x > 10 || ver.y > 13;*/
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 674b0352..a89eb0eb 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -4799,64 +4799,67 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
4799 }; 4799 };
4800 init_Paint(&ctx.paint); 4800 init_Paint(&ctx.paint);
4801 render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); 4801 render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */);
4802 setClip_Paint(&ctx.paint, clipBounds); 4802 int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY);
4803 int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY); 4803 const iBool isDocEmpty = size_GmDocument(d->doc).y == 0;
4804 draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds));
4805 /* Text markers. */
4806 const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; 4804 const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0;
4807 if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) { 4805 if (!isDocEmpty) {
4808 SDL_Renderer *render = renderer_Window(get_Window()); 4806 setClip_Paint(&ctx.paint, clipBounds);
4809 ctx.firstMarkRect = zero_Rect(); 4807 draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds));
4810 ctx.lastMarkRect = zero_Rect(); 4808 /* Text markers. */
4811 SDL_SetRenderDrawBlendMode(render, 4809 if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) {
4812 isDark_ColorTheme(colorTheme_App()) ? SDL_BLENDMODE_ADD 4810 SDL_Renderer *render = renderer_Window(get_Window());
4813 : SDL_BLENDMODE_BLEND); 4811 ctx.firstMarkRect = zero_Rect();
4814 ctx.viewPos = topLeft_Rect(docBounds); 4812 ctx.lastMarkRect = zero_Rect();
4815 /* Marker starting outside the visible range? */ 4813 SDL_SetRenderDrawBlendMode(render,
4816 if (d->visibleRuns.start) { 4814 isDark_ColorTheme(colorTheme_App()) ? SDL_BLENDMODE_ADD
4817 if (!isEmpty_Range(&d->selectMark) && 4815 : SDL_BLENDMODE_BLEND);
4818 d->selectMark.start < d->visibleRuns.start->text.start && 4816 ctx.viewPos = topLeft_Rect(docBounds);
4819 d->selectMark.end > d->visibleRuns.start->text.start) { 4817 /* Marker starting outside the visible range? */
4820 ctx.inSelectMark = iTrue; 4818 if (d->visibleRuns.start) {
4819 if (!isEmpty_Range(&d->selectMark) &&
4820 d->selectMark.start < d->visibleRuns.start->text.start &&
4821 d->selectMark.end > d->visibleRuns.start->text.start) {
4822 ctx.inSelectMark = iTrue;
4823 }
4824 if (isEmpty_Range(&d->foundMark) &&
4825 d->foundMark.start < d->visibleRuns.start->text.start &&
4826 d->foundMark.end > d->visibleRuns.start->text.start) {
4827 ctx.inFoundMark = iTrue;
4828 }
4821 } 4829 }
4822 if (isEmpty_Range(&d->foundMark) && 4830 render_GmDocument(d->doc, vis, drawMark_DrawContext_, &ctx);
4823 d->foundMark.start < d->visibleRuns.start->text.start && 4831 SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE);
4824 d->foundMark.end > d->visibleRuns.start->text.start) { 4832 /* Selection range pins. */
4825 ctx.inFoundMark = iTrue; 4833 if (isTouchSelecting) {
4834 drawPin_(&ctx.paint, ctx.firstMarkRect, 0);
4835 drawPin_(&ctx.paint, ctx.lastMarkRect, 1);
4826 } 4836 }
4827 } 4837 }
4828 render_GmDocument(d->doc, vis, drawMark_DrawContext_, &ctx); 4838 drawMedia_DocumentWidget_(d, &ctx.paint);
4829 SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); 4839 /* Fill the top and bottom, in case the document is short. */
4830 /* Selection range pins. */ 4840 if (yTop > top_Rect(bounds)) {
4831 if (isTouchSelecting) { 4841 fillRect_Paint(&ctx.paint,
4832 drawPin_(&ctx.paint, ctx.firstMarkRect, 0); 4842 (iRect){ bounds.pos, init_I2(bounds.size.x, yTop - top_Rect(bounds)) },
4833 drawPin_(&ctx.paint, ctx.lastMarkRect, 1); 4843 hasSiteBanner_GmDocument(d->doc) ? tmBannerBackground_ColorId
4834 } 4844 : tmBackground_ColorId);
4835 } 4845 }
4836 drawMedia_DocumentWidget_(d, &ctx.paint); 4846 const int yBottom = yTop + size_GmDocument(d->doc).y + 1;
4837 /* Fill the top and bottom, in case the document is short. */ 4847 if (yBottom < bottom_Rect(bounds)) {
4838 if (yTop > top_Rect(bounds)) { 4848 fillRect_Paint(&ctx.paint,
4839 fillRect_Paint(&ctx.paint, 4849 init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom),
4840 (iRect){ bounds.pos, init_I2(bounds.size.x, yTop - top_Rect(bounds)) }, 4850 tmBackground_ColorId);
4841 hasSiteBanner_GmDocument(d->doc) ? tmBannerBackground_ColorId 4851 }
4842 : tmBackground_ColorId); 4852 unsetClip_Paint(&ctx.paint);
4843 } 4853 drawSideElements_DocumentWidget_(d);
4844 const int yBottom = yTop + size_GmDocument(d->doc).y + 1; 4854 if (prefs_App()->hoverLink && d->hoverLink) {
4845 if (yBottom < bottom_Rect(bounds)) { 4855 const int font = uiLabel_FontId;
4846 fillRect_Paint(&ctx.paint, 4856 const iRangecc linkUrl = range_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId));
4847 init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom), 4857 const iInt2 size = measureRange_Text(font, linkUrl).bounds.size;
4848 tmBackground_ColorId); 4858 const iRect linkRect = { addY_I2(bottomLeft_Rect(bounds), -size.y),
4849 } 4859 addX_I2(size, 2 * gap_UI) };
4850 unsetClip_Paint(&ctx.paint); 4860 fillRect_Paint(&ctx.paint, linkRect, tmBackground_ColorId);
4851 drawSideElements_DocumentWidget_(d); 4861 drawRange_Text(font, addX_I2(topLeft_Rect(linkRect), gap_UI), tmParagraph_ColorId, linkUrl);
4852 if (prefs_App()->hoverLink && d->hoverLink) { 4862 }
4853 const int font = uiLabel_FontId;
4854 const iRangecc linkUrl = range_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId));
4855 const iInt2 size = measureRange_Text(font, linkUrl).bounds.size;
4856 const iRect linkRect = { addY_I2(bottomLeft_Rect(bounds), -size.y),
4857 addX_I2(size, 2 * gap_UI) };
4858 fillRect_Paint(&ctx.paint, linkRect, tmBackground_ColorId);
4859 drawRange_Text(font, addX_I2(topLeft_Rect(linkRect), gap_UI), tmParagraph_ColorId, linkUrl);
4860 } 4863 }
4861 if (colorTheme_App() == pureWhite_ColorTheme) { 4864 if (colorTheme_App() == pureWhite_ColorTheme) {
4862 drawHLine_Paint(&ctx.paint, topLeft_Rect(bounds), width_Rect(bounds), uiSeparator_ColorId); 4865 drawHLine_Paint(&ctx.paint, topLeft_Rect(bounds), width_Rect(bounds), uiSeparator_ColorId);
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index f1f368b6..9f233345 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -691,7 +691,6 @@ void setMode_InputWidget(iInputWidget *d, enum iInputMode mode) {
691 d->mode = mode; 691 d->mode = mode;
692} 692}
693 693
694#if 0
695static void restoreDefaultScheme_(iString *url) { 694static void restoreDefaultScheme_(iString *url) {
696 iUrl parts; 695 iUrl parts;
697 init_Url(&parts, url); 696 init_Url(&parts, url);
@@ -706,17 +705,14 @@ static const iString *omitDefaultScheme_(iString *url) {
706 } 705 }
707 return url; 706 return url;
708} 707}
709#endif
710 708
711const iString *text_InputWidget(const iInputWidget *d) { 709const iString *text_InputWidget(const iInputWidget *d) {
712 if (d) { 710 if (d) {
713 iString *text = collect_String(text_InputWidget_(d)); 711 iString *text = collect_String(text_InputWidget_(d));
714#if 0
715 if (d->inFlags & isUrl_InputWidgetFlag) { 712 if (d->inFlags & isUrl_InputWidgetFlag) {
716 /* Add the "gemini" scheme back if one is omitted. */ 713 /* Add the "gemini" scheme back if one is omitted. */
717 restoreDefaultScheme_(text); 714 restoreDefaultScheme_(text);
718 } 715 }
719#endif
720 return text; 716 return text;
721 } 717 }
722 return collectNew_String(); 718 return collectNew_String();
@@ -820,6 +816,22 @@ static void updateBuffered_InputWidget_(iInputWidget *d) {
820 for (int i = visRange.start; i < visRange.end; i++) { 816 for (int i = visRange.start; i < visRange.end; i++) {
821 append_String(visText, &line_InputWidget_(d, i)->text); 817 append_String(visText, &line_InputWidget_(d, i)->text);
822 } 818 }
819 if (d->inFlags & isUrl_InputWidgetFlag) {
820 /* Highlight the host name. */
821 iUrl parts;
822 init_Url(&parts, visText);
823 if (!isEmpty_Range(&parts.host)) {
824 const char *cstr = cstr_String(visText);
825 insertData_Block(&visText->chars,
826 parts.host.end - cstr,
827 restore_ColorEscape,
828 strlen(restore_ColorEscape));
829 insertData_Block(&visText->chars,
830 parts.host.start - cstr,
831 uiTextStrong_ColorEscape,
832 strlen(uiTextStrong_ColorEscape));
833 }
834 }
823 iWrapText wt = wrap_InputWidget_(d, 0); 835 iWrapText wt = wrap_InputWidget_(d, 0);
824 wt.text = range_String(visText); 836 wt.text = range_String(visText);
825 const int fg = uiInputText_ColorId; 837 const int fg = uiInputText_ColorId;
@@ -853,12 +865,10 @@ void setText_InputWidget(iInputWidget *d, const iString *text) {
853 punyEncodeUrlHost_String(enc); 865 punyEncodeUrlHost_String(enc);
854 text = enc; 866 text = enc;
855 } 867 }
856#if 0
857 /* Omit the default (Gemini) scheme if there isn't much space. */ 868 /* Omit the default (Gemini) scheme if there isn't much space. */
858 if (isNarrow_Root(as_Widget(d)->root)) { 869 if (isNarrow_Root(as_Widget(d)->root)) {
859 text = omitDefaultScheme_(collect_String(copy_String(text))); 870 text = omitDefaultScheme_(collect_String(copy_String(text)));
860 } 871 }
861#endif
862 } 872 }
863 clearUndo_InputWidget_(d); 873 clearUndo_InputWidget_(d);
864 iString *nfcText = collect_String(copy_String(text)); 874 iString *nfcText = collect_String(copy_String(text));
@@ -873,10 +883,6 @@ void setText_InputWidget(iInputWidget *d, const iString *text) {
873 if (!isFocused_Widget(d)) { 883 if (!isFocused_Widget(d)) {
874 iZap(d->mark); 884 iZap(d->mark);
875 } 885 }
876// else {
877// d->cursor.y = iMin(d->cursor.y, (int) size_Array(&d->lines) - 1);
878// d->cursor.x = iMin(d->cursor.x, size_String(&cursorLine_InputWidget_(d)->text));
879// }
880 if (!isFocused_Widget(d)) { 886 if (!isFocused_Widget(d)) {
881 d->inFlags |= needUpdateBuffer_InputWidgetFlag; 887 d->inFlags |= needUpdateBuffer_InputWidgetFlag;
882 } 888 }
@@ -1414,12 +1420,10 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
1414 /* Resize according to width immediately. */ 1420 /* Resize according to width immediately. */
1415 if (d->lastUpdateWidth != w->rect.size.x) { 1421 if (d->lastUpdateWidth != w->rect.size.x) {
1416 d->inFlags |= needUpdateBuffer_InputWidgetFlag; 1422 d->inFlags |= needUpdateBuffer_InputWidgetFlag;
1417#if 0
1418 if (d->inFlags & isUrl_InputWidgetFlag) { 1423 if (d->inFlags & isUrl_InputWidgetFlag) {
1419 /* Restore/omit the default scheme if necessary. */ 1424 /* Restore/omit the default scheme if necessary. */
1420 setText_InputWidget(d, text_InputWidget(d)); 1425 setText_InputWidget(d, text_InputWidget(d));
1421 } 1426 }
1422#endif
1423 updateAllLinesAndResizeHeight_InputWidget_(d); 1427 updateAllLinesAndResizeHeight_InputWidget_(d);
1424 d->lastUpdateWidth = w->rect.size.x; 1428 d->lastUpdateWidth = w->rect.size.x;
1425 } 1429 }
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index a9a2d033..edc36a49 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -30,6 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
30#include "keys.h" 30#include "keys.h"
31#include "touch.h" 31#include "touch.h"
32 32
33#include <SDL_version.h>
34
33struct Impl_LabelWidget { 35struct Impl_LabelWidget {
34 iWidget widget; 36 iWidget widget;
35 iString srcLabel; 37 iString srcLabel;
@@ -295,9 +297,18 @@ static void draw_LabelWidget_(const iLabelWidget *d) {
295 iRect frameRect = adjusted_Rect(rect, zero_I2(), init1_I2(-1)); 297 iRect frameRect = adjusted_Rect(rect, zero_I2(), init1_I2(-1));
296 if (isButton) { 298 if (isButton) {
297 iInt2 points[] = { 299 iInt2 points[] = {
298 bottomLeft_Rect(frameRect), topLeft_Rect(frameRect), topRight_Rect(frameRect), 300 bottomLeft_Rect(frameRect),
299 bottomRight_Rect(frameRect), bottomLeft_Rect(frameRect) 301 topLeft_Rect(frameRect),
302 topRight_Rect(frameRect),
303 bottomRight_Rect(frameRect),
304 bottomLeft_Rect(frameRect)
300 }; 305 };
306#if SDL_VERSION_ATLEAST(2, 0, 16)
307 if (isOpenGLRenderer_Window()) {
308 /* A very curious regression in SDL 2.0.16. */
309 points[3].x--;
310 }
311#endif
301 drawLines_Paint(&p, points + 2, 3, frame2); 312 drawLines_Paint(&p, points + 2, 3, frame2);
302 drawLines_Paint( 313 drawLines_Paint(
303 &p, points, !isHover && flags & d->flags.noTopFrame ? 2 : 3, frame); 314 &p, points, !isHover && flags & d->flags.noTopFrame ? 2 : 3, frame);
diff --git a/src/ui/paint.c b/src/ui/paint.c
index c575d5fc..79adb7d1 100644
--- a/src/ui/paint.c
+++ b/src/ui/paint.c
@@ -89,13 +89,19 @@ void drawRect_Paint(const iPaint *d, iRect rect, int color) {
89 /* Keep the right/bottom edge visible in the window. */ 89 /* Keep the right/bottom edge visible in the window. */
90 if (br.x == d->dst->size.x) br.x--; 90 if (br.x == d->dst->size.x) br.x--;
91 if (br.y == d->dst->size.y) br.y--; 91 if (br.y == d->dst->size.y) br.y--;
92 const SDL_Point edges[] = { 92 SDL_Point edges[] = {
93 { left_Rect(rect), top_Rect(rect) }, 93 { left_Rect(rect), top_Rect(rect) },
94 { br.x, top_Rect(rect) }, 94 { br.x, top_Rect(rect) },
95 { br.x, br.y }, 95 { br.x, br.y },
96 { left_Rect(rect), br.y }, 96 { left_Rect(rect), br.y },
97 { left_Rect(rect), top_Rect(rect) } 97 { left_Rect(rect), top_Rect(rect) }
98 }; 98 };
99#if SDL_VERSION_ATLEAST(2, 0, 16)
100 if (isOpenGLRenderer_Window()) {
101 /* A very curious regression in SDL 2.0.16. */
102 edges[3].y--;
103 }
104#endif
99 setColor_Paint_(d, color); 105 setColor_Paint_(d, color);
100 SDL_RenderDrawLines(renderer_Paint_(d), edges, iElemCount(edges)); 106 SDL_RenderDrawLines(renderer_Paint_(d), edges, iElemCount(edges));
101} 107}
diff --git a/src/ui/visbuf.c b/src/ui/visbuf.c
index 88a5fd60..503d0a2f 100644
--- a/src/ui/visbuf.c
+++ b/src/ui/visbuf.c
@@ -58,18 +58,14 @@ void alloc_VisBuf(iVisBuf *d, const iInt2 size, int granularity) {
58 if (tex->texture) { 58 if (tex->texture) {
59 SDL_DestroyTexture(tex->texture); 59 SDL_DestroyTexture(tex->texture);
60 } 60 }
61 SDL_Renderer *rend = renderer_Window(get_Window());
61 tex->texture = 62 tex->texture =
62 SDL_CreateTexture(renderer_Window(get_Window()), 63 SDL_CreateTexture(rend,
63 SDL_PIXELFORMAT_RGBA8888, 64 SDL_PIXELFORMAT_RGBA8888,
64 SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, 65 SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET,
65 texSize.x, 66 texSize.x,
66 texSize.y); 67 texSize.y);
67 SDL_SetTextureBlendMode(tex->texture, SDL_BLENDMODE_NONE); 68 SDL_SetTextureBlendMode(tex->texture, SDL_BLENDMODE_NONE);
68// tex->origin = i * texSize.y;
69// iZap(tex->validRange);
70// if (d->invalidUserData) {
71// d->invalidUserData(i, d->buffers[i].user);
72// }
73 } 69 }
74 invalidate_VisBuf(d); 70 invalidate_VisBuf(d);
75 } 71 }
diff --git a/src/ui/window.c b/src/ui/window.c
index 6b8abb4d..86d22b1c 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -65,6 +65,8 @@ static float initialUiScale_ = 1.0f;
65static float initialUiScale_ = 1.1f; 65static float initialUiScale_ = 1.1f;
66#endif 66#endif
67 67
68static iBool isOpenGLRenderer_;
69
68iDefineTypeConstructionArgs(Window, (iRect rect), rect) 70iDefineTypeConstructionArgs(Window, (iRect rect), rect)
69 71
70/* TODO: Define menus per platform. */ 72/* TODO: Define menus per platform. */
@@ -446,6 +448,7 @@ void init_Window(iWindow *d, iRect rect) {
446 /* Some info. */ { 448 /* Some info. */ {
447 SDL_RendererInfo info; 449 SDL_RendererInfo info;
448 SDL_GetRendererInfo(d->render, &info); 450 SDL_GetRendererInfo(d->render, &info);
451 isOpenGLRenderer_ = !iCmpStr(info.name, "opengl");
449 printf("[window] renderer: %s%s\n", info.name, 452 printf("[window] renderer: %s%s\n", info.name,
450 info.flags & SDL_RENDERER_ACCELERATED ? " (accelerated)" : ""); 453 info.flags & SDL_RENDERER_ACCELERATED ? " (accelerated)" : "");
451#if !defined (NDEBUG) 454#if !defined (NDEBUG)
@@ -1187,6 +1190,10 @@ iWindow *get_Window(void) {
1187 return theWindow_; 1190 return theWindow_;
1188} 1191}
1189 1192
1193iBool isOpenGLRenderer_Window(void) {
1194 return isOpenGLRenderer_;
1195}
1196
1190void setKeyboardHeight_Window(iWindow *d, int height) { 1197void setKeyboardHeight_Window(iWindow *d, int height) {
1191 if (d->keyboardHeight != height) { 1198 if (d->keyboardHeight != height) {
1192 d->keyboardHeight = height; 1199 d->keyboardHeight = height;
diff --git a/src/ui/window.h b/src/ui/window.h
index bb98dbe9..63f7e5f2 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -137,6 +137,8 @@ iRoot * findRoot_Window (const iWindow *, const iWidget *widget);
137iRoot * otherRoot_Window (const iWindow *, iRoot *root); 137iRoot * otherRoot_Window (const iWindow *, iRoot *root);
138iWindow * get_Window (void); 138iWindow * get_Window (void);
139 139
140iBool isOpenGLRenderer_Window (void);
141
140#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 142#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
141SDL_HitTestResult hitTest_Window(const iWindow *d, iInt2 pos); 143SDL_HitTestResult hitTest_Window(const iWindow *d, iInt2 pos);
142#endif 144#endif