diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-21 17:32:58 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-21 17:32:58 +0300 |
commit | d623d42d937d8e7f90deda3f49fe1eb680a7b15e (patch) | |
tree | f0d917942765701ce1a8b374994a640e96b079cc | |
parent | 2fe3ce64df1924f13a9292e4df013c55e06e3ad1 (diff) |
DocumentWidget: Fetching URL contents
-rw-r--r-- | .clang-format | 107 | ||||
-rw-r--r-- | src/app.c | 2 | ||||
-rw-r--r-- | src/ui/command.c | 2 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 118 | ||||
-rw-r--r-- | src/ui/documentwidget.h | 2 |
5 files changed, 229 insertions, 2 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..0563a742 --- /dev/null +++ b/.clang-format | |||
@@ -0,0 +1,107 @@ | |||
1 | Language: Cpp | ||
2 | # BasedOnStyle: Mozilla | ||
3 | AccessModifierOffset: -4 | ||
4 | AlignAfterOpenBracket: Align | ||
5 | AlignConsecutiveAssignments: true | ||
6 | AlignConsecutiveDeclarations: true | ||
7 | AlignEscapedNewlines: Right | ||
8 | AlignOperands: true | ||
9 | AlignTrailingComments: true | ||
10 | AllowAllParametersOfDeclarationOnNextLine: false | ||
11 | AllowShortBlocksOnASingleLine: false | ||
12 | AllowShortCaseLabelsOnASingleLine: false | ||
13 | AllowShortFunctionsOnASingleLine: Inline | ||
14 | AllowShortIfStatementsOnASingleLine: true | ||
15 | AllowShortLoopsOnASingleLine: false | ||
16 | AlwaysBreakAfterDefinitionReturnType: None | ||
17 | AlwaysBreakAfterReturnType: None | ||
18 | AlwaysBreakBeforeMultilineStrings: false | ||
19 | AlwaysBreakTemplateDeclarations: false | ||
20 | BinPackArguments: false | ||
21 | BinPackParameters: true | ||
22 | BraceWrapping: | ||
23 | AfterClass: false | ||
24 | AfterControlStatement: false | ||
25 | AfterEnum: false | ||
26 | AfterFunction: false | ||
27 | AfterNamespace: false | ||
28 | AfterObjCDeclaration: false | ||
29 | AfterStruct: false | ||
30 | AfterUnion: false | ||
31 | BeforeCatch: true | ||
32 | BeforeElse: true | ||
33 | IndentBraces: false | ||
34 | SplitEmptyFunction: true | ||
35 | SplitEmptyRecord: false | ||
36 | SplitEmptyNamespace: true | ||
37 | BreakBeforeBinaryOperators: None | ||
38 | BreakBeforeBraces: Custom | ||
39 | BreakBeforeInheritanceComma: true | ||
40 | BreakBeforeTernaryOperators: true | ||
41 | BreakConstructorInitializersBeforeComma: true | ||
42 | BreakConstructorInitializers: BeforeComma | ||
43 | BreakAfterJavaFieldAnnotations: false | ||
44 | BreakStringLiterals: true | ||
45 | ColumnLimit: 100 | ||
46 | CommentPragmas: '^ IWYU pragma:' | ||
47 | CompactNamespaces: true | ||
48 | ConstructorInitializerAllOnOneLineOrOnePerLine: false | ||
49 | ConstructorInitializerIndentWidth: 4 | ||
50 | ContinuationIndentWidth: 4 | ||
51 | Cpp11BracedListStyle: false | ||
52 | DerivePointerAlignment: false | ||
53 | DisableFormat: false | ||
54 | ExperimentalAutoDetectBinPacking: false | ||
55 | FixNamespaceComments: true | ||
56 | ForEachMacros: | ||
57 | - foreach | ||
58 | - Q_FOREACH | ||
59 | - BOOST_FOREACH | ||
60 | #IncludeBlocks: Preserve | ||
61 | IncludeCategories: | ||
62 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' | ||
63 | Priority: 2 | ||
64 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' | ||
65 | Priority: 3 | ||
66 | - Regex: '.*' | ||
67 | Priority: 1 | ||
68 | IncludeIsMainRegex: '(Test)?$' | ||
69 | IndentCaseLabels: true | ||
70 | #IndentPPDirectives: AfterHash | ||
71 | IndentWidth: 4 | ||
72 | IndentWrappedFunctionNames: false | ||
73 | JavaScriptQuotes: Leave | ||
74 | JavaScriptWrapImports: true | ||
75 | KeepEmptyLinesAtTheStartOfBlocks: true | ||
76 | MacroBlockBegin: '' | ||
77 | MacroBlockEnd: '' | ||
78 | MaxEmptyLinesToKeep: 1 | ||
79 | NamespaceIndentation: None | ||
80 | ObjCBlockIndentWidth: 2 | ||
81 | ObjCSpaceAfterProperty: true | ||
82 | ObjCSpaceBeforeProtocolList: false | ||
83 | PenaltyBreakAssignment: 2 | ||
84 | PenaltyBreakBeforeFirstCallParameter: 19 | ||
85 | PenaltyBreakComment: 300 | ||
86 | PenaltyBreakFirstLessLess: 120 | ||
87 | PenaltyBreakString: 1000 | ||
88 | PenaltyExcessCharacter: 1000000 | ||
89 | PenaltyReturnTypeOnItsOwnLine: 200 | ||
90 | PointerAlignment: Right | ||
91 | ReflowComments: true | ||
92 | SortIncludes: true | ||
93 | SortUsingDeclarations: true | ||
94 | SpaceAfterCStyleCast: true | ||
95 | SpaceAfterTemplateKeyword: true | ||
96 | SpaceBeforeAssignmentOperators: true | ||
97 | SpaceBeforeParens: ControlStatements | ||
98 | SpaceInEmptyParentheses: false | ||
99 | SpacesBeforeTrailingComments: 1 | ||
100 | SpacesInAngles: false | ||
101 | SpacesInContainerLiterals: true | ||
102 | SpacesInCStyleCastParentheses: false | ||
103 | SpacesInParentheses: false | ||
104 | SpacesInSquareBrackets: false | ||
105 | Standard: Cpp11 | ||
106 | TabWidth: 4 | ||
107 | UseTab: Never | ||
@@ -101,7 +101,7 @@ static void loadPrefs_App_(iApp *d) { | |||
101 | iRangecc line = iNullRange; | 101 | iRangecc line = iNullRange; |
102 | while (nextSplit_Rangecc(&src, "\n", &line)) { | 102 | while (nextSplit_Rangecc(&src, "\n", &line)) { |
103 | iString cmd; | 103 | iString cmd; |
104 | initRange_String(&cmd, &line); | 104 | initRange_String(&cmd, line); |
105 | if (equal_Command(cstr_String(&cmd), "uiscale")) { | 105 | if (equal_Command(cstr_String(&cmd), "uiscale")) { |
106 | /* Must be handled before the window is created. */ | 106 | /* Must be handled before the window is created. */ |
107 | setUiScale_Window(get_Window(), argf_Command(cstr_String(&cmd))); | 107 | setUiScale_Window(get_Window(), argf_Command(cstr_String(&cmd))); |
diff --git a/src/ui/command.c b/src/ui/command.c index 16a0d948..71228784 100644 --- a/src/ui/command.c +++ b/src/ui/command.c | |||
@@ -64,7 +64,7 @@ const iString *string_Command(const char *cmd, const char *label) { | |||
64 | iRangecc val = { valuePtr_Command(cmd, label), NULL }; | 64 | iRangecc val = { valuePtr_Command(cmd, label), NULL }; |
65 | if (val.start) { | 65 | if (val.start) { |
66 | for (val.end = val.start; *val.end && !isspace(*val.end); val.end++) {} | 66 | for (val.end = val.start; *val.end && !isspace(*val.end); val.end++) {} |
67 | return collect_String(newRange_String(&val)); | 67 | return collect_String(newRange_String(val)); |
68 | } | 68 | } |
69 | return collectNew_String(); | 69 | return collectNew_String(); |
70 | } | 70 | } |
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 | |||
7 | enum iDocumentState { | ||
8 | blank_DocumentState, | ||
9 | fetching_DocumentState, | ||
10 | layout_DocumentState, | ||
11 | ready_DocumentState, | ||
12 | }; | ||
13 | |||
4 | struct Impl_DocumentWidget { | 14 | struct 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 | ||
23 | iDeclareType(Url) | ||
24 | |||
25 | struct Impl_Url { | ||
26 | iRangecc protocol; | ||
27 | iRangecc host; | ||
28 | iRangecc port; | ||
29 | iRangecc path; | ||
30 | iRangecc query; | ||
31 | }; | ||
32 | |||
33 | void 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 | |||
8 | iDefineObjectConstruction(DocumentWidget) | 53 | iDefineObjectConstruction(DocumentWidget) |
9 | 54 | ||
10 | void init_DocumentWidget(iDocumentWidget *d) { | 55 | void 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 | ||
16 | void deinit_DocumentWidget(iDocumentWidget *d) { | 68 | void deinit_DocumentWidget(iDocumentWidget *d) { |
69 | delete_String(d->source); | ||
70 | delete_String(d->url); | ||
71 | } | ||
72 | |||
73 | void 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 | |||
80 | static 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 | |||
86 | static 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 | |||
105 | static 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 | ||
126 | void 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 | ||
20 | static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { | 136 | static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { |
@@ -25,6 +141,8 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
25 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | 141 | static 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 | ||
30 | iBeginDefineSubclass(DocumentWidget, Widget) | 148 | iBeginDefineSubclass(DocumentWidget, Widget) |
diff --git a/src/ui/documentwidget.h b/src/ui/documentwidget.h index e03e08e4..02b75e69 100644 --- a/src/ui/documentwidget.h +++ b/src/ui/documentwidget.h | |||
@@ -4,3 +4,5 @@ | |||
4 | 4 | ||
5 | iDeclareWidgetClass(DocumentWidget) | 5 | iDeclareWidgetClass(DocumentWidget) |
6 | iDeclareObjectConstruction(DocumentWidget) | 6 | iDeclareObjectConstruction(DocumentWidget) |
7 | |||
8 | void setUrl_DocumentWidget (iDocumentWidget *d, const iString *url); | ||