summaryrefslogtreecommitdiff
path: root/src/ui/documentwidget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r--src/ui/documentwidget.c118
1 files changed, 118 insertions, 0 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 31c1a14c..e8a0ded9 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1,20 +1,136 @@
1#include "documentwidget.h" 1#include "documentwidget.h"
2#include "paint.h" 2#include "paint.h"
3 3
4#include <the_Foundation/regexp.h>
5#include <the_Foundation/tlsrequest.h>
6
7enum iDocumentState {
8 blank_DocumentState,
9 fetching_DocumentState,
10 layout_DocumentState,
11 ready_DocumentState,
12};
13
4struct Impl_DocumentWidget { 14struct Impl_DocumentWidget {
5 iWidget widget; 15 iWidget widget;
16 enum iDocumentState state;
17 iString *url;
18 iString *source;
19 int statusCode;
20 iTlsRequest *request;
6}; 21};
7 22
23iDeclareType(Url)
24
25struct Impl_Url {
26 iRangecc protocol;
27 iRangecc host;
28 iRangecc port;
29 iRangecc path;
30 iRangecc query;
31};
32
33void init_Url(iUrl *d, const iString *text) {
34 iRegExp *pattern = new_RegExp("(.+)://([^/:?]+)(:[0-9]+)?([^?]*)(\\?.*)?", caseInsensitive_RegExpOption);
35 iRegExpMatch m;
36 if (matchString_RegExp(pattern, text, &m)) {
37 capturedRange_RegExpMatch(&m, 1, &d->protocol);
38 capturedRange_RegExpMatch(&m, 2, &d->host);
39 capturedRange_RegExpMatch(&m, 3, &d->port);
40 if (!isEmpty_Range(&d->port)) {
41 /* Don't include the colon. */
42 d->port.start++;
43 }
44 capturedRange_RegExpMatch(&m, 4, &d->path);
45 capturedRange_RegExpMatch(&m, 5, &d->query);
46 }
47 else {
48 iZap(*d);
49 }
50 iRelease(pattern);
51}
52
8iDefineObjectConstruction(DocumentWidget) 53iDefineObjectConstruction(DocumentWidget)
9 54
10void init_DocumentWidget(iDocumentWidget *d) { 55void init_DocumentWidget(iDocumentWidget *d) {
11 iWidget *w = as_Widget(d); 56 iWidget *w = as_Widget(d);
12 init_Widget(w); 57 init_Widget(w);
13 setBackgroundColor_Widget(w, gray25_ColorId); 58 setBackgroundColor_Widget(w, gray25_ColorId);
59 d->state = blank_DocumentState;
60 d->url = new_String();
61 d->statusCode = 0;
62 d->source = new_String();
63 d->request = NULL;
64
65 setUrl_DocumentWidget(d, collectNewCStr_String("gemini.circumlunar.space/"));
14} 66}
15 67
16void deinit_DocumentWidget(iDocumentWidget *d) { 68void deinit_DocumentWidget(iDocumentWidget *d) {
69 delete_String(d->source);
70 delete_String(d->url);
71}
72
73void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) {
74 /* TODO: lock source during update */
75 set_String(d->source, source);
76 printf("%s\n", cstr_String(d->source));
77 d->state = layout_DocumentState;
78}
79
80static iRangecc getLine_(iRangecc text) {
81 iRangecc line = { text.start, text.start };
82 for (; *line.end != '\n' && line.end != text.end; line.end++) {}
83 return line;
84}
85
86static void requestFinished_DocumentWidget_(iAnyObject *obj) {
87 iDocumentWidget *d = obj;
88 iBlock *response = readAll_TlsRequest(d->request);
89 iRangecc responseRange = { constBegin_Block(response), constEnd_Block(response) };
90 iRangecc respLine = getLine_(responseRange);
91 responseRange.start = respLine.end + 1;
92 /* First line is the status code. */ {
93 iString *line = newRange_String(respLine);
94 trim_String(line);
95 printf("response: %s\n", cstr_String(line));
96 delete_String(line);
97 }
98 setSource_DocumentWidget(d, collect_String(newRange_String(responseRange)));
99 delete_Block(response);
100 iReleaseLater(d->request);
101 d->request = NULL;
102 fflush(stdout);
103}
104
105static void fetch_DocumentWidget_(iDocumentWidget *d) {
106 iAssert(!d->request);
107 d->state = fetching_DocumentState;
108 d->statusCode = 0;
109 iUrl url;
110 init_Url(&url, d->url);
111 d->request = new_TlsRequest();
112 uint16_t port = toInt_String(collect_String(newRange_String(url.port)));
113 if (port == 0) {
114 port = 1965; /* default Gemini port */
115 }
116 setUrl_TlsRequest(d->request, collect_String(newRange_String(url.host)), port);
117 /* The request string is an UTF-8 encoded absolute URL. */
118 iString *content = collectNew_String();
119 append_String(content, d->url);
120 appendCStr_String(content, "\r\n");
121 setContent_TlsRequest(d->request, utf8_String(content));
122 iConnect(TlsRequest, d->request, finished, d, requestFinished_DocumentWidget_);
123 submit_TlsRequest(d->request);
124}
17 125
126void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) {
127 clear_String(d->url);
128 if (indexOfCStr_String(url, "://") == iInvalidPos && !startsWithCase_String(url, "gemini:")) {
129 /* Prepend default protocol. */
130 setCStr_String(d->url, "gemini://");
131 }
132 append_String(d->url, url);
133 fetch_DocumentWidget_(d);
18} 134}
19 135
20static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { 136static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) {
@@ -25,6 +141,8 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
25static void draw_DocumentWidget_(const iDocumentWidget *d) { 141static void draw_DocumentWidget_(const iDocumentWidget *d) {
26 const iWidget *w = constAs_Widget(d); 142 const iWidget *w = constAs_Widget(d);
27 draw_Widget(w); 143 draw_Widget(w);
144 if (d->state != ready_DocumentState) return;
145 /* TODO: lock source during draw */
28} 146}
29 147
30iBeginDefineSubclass(DocumentWidget, Widget) 148iBeginDefineSubclass(DocumentWidget, Widget)