diff options
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | ag_render.f90 | 175 | ||||
-rw-r--r-- | dumb_render.f90 | 101 | ||||
-rw-r--r-- | favicon.f90 | 114 | ||||
-rw-r--r-- | files.f90 | 2 | ||||
-rw-r--r-- | gemini-windows.prj | 15 | ||||
-rw-r--r-- | gemini.prj | 10 | ||||
-rw-r--r-- | internal.F90 | 7 | ||||
-rw-r--r-- | layout.f90 | 15 | ||||
-rw-r--r-- | main.F90 | 46 | ||||
-rw-r--r-- | makefile.gnu | 19 | ||||
-rw-r--r-- | platform.F90 | 8 | ||||
-rw-r--r-- | protocol.f90 | 11 | ||||
-rw-r--r-- | render.f90 | 60 | ||||
-rw-r--r-- | request.f90 | 16 | ||||
-rw-r--r-- | snap/snapcraft.yaml | 2 |
16 files changed, 504 insertions, 104 deletions
@@ -25,10 +25,13 @@ If you want to try out LR-87, you have a few choices: ### Windows +LR-87 is available in the [Windows Store](https://www.microsoft.com/en-us/p/lr-87-a-gemini-browser/9njrwpxrwhj2) +for Windows 10. + Builds for Windows are available at [https://bin.rainbow-100.com](https://bin.rainbow-100.com) as a simple Zipfile. These builds are not signed, so modern Windows will likely -bitch about that. +bitch about that. I also don't update these regularly because of the link above. ### GNU/Linux @@ -90,5 +93,5 @@ AppGraphics library from Simply Fortran. ## License -LR-87 is Copyright (c) 2020 Jeffrey Armstrong <jeff@rainbow-100.com>, and the software +LR-87 is Copyright (c) 2020-2022 Jeffrey Armstrong <jeff@rainbow-100.com>, and the software is licensed under the JSON license. See LICENSE.txt for more details. diff --git a/ag_render.f90 b/ag_render.f90 index bddec84..b91a416 100644 --- a/ag_render.f90 +++ b/ag_render.f90 @@ -34,6 +34,7 @@ implicit none integer, volatile::ag_render_event integer, volatile::scroll_position + integer, volatile::wheel_movement integer, parameter::ag_render_event_none = 0 integer, parameter::ag_render_event_closed = 1 @@ -85,12 +86,18 @@ implicit none ! Needed to compute document height integer::initial_y + + ! Page height + integer::page_height ! An error has occurred, so render blank logical::render_blank ! A title was guessed and set logical::title_guessed + + ! Text of the title + character(len=256)::title_text contains @@ -131,6 +138,11 @@ implicit none procedure :: report_unsupported_protocol => ag_report_unsupported_protocol + procedure :: set_favicon => ag_set_favicon + + procedure :: rendering_height_available => ag_rendering_height_available + procedure :: scroll_max_value => ag_scroll_max_value + end type appgraphics_renderer contains @@ -209,13 +221,7 @@ contains integer::x, y - scroll_position = scroll_position - (x/35) - if(scroll_position < 0) then - scroll_position = 0 - else if(scroll_position > 100) then - scroll_position = 100 - end if - + wheel_movement = wheel_movement + x ag_render_event = ag_render_event_wheel call stopidle() @@ -226,6 +232,7 @@ contains implicit none integer::x + scroll_position = x ag_render_event = ag_render_event_scroll call stopidle() @@ -241,6 +248,18 @@ contains end subroutine resize_callback + subroutine ag_set_favicon(self, f) + implicit none + + class(appgraphics_renderer)::self + character(*), intent(in)::f + + self%favicon = f + + call set_window_title(self, self%title_text) + + end subroutine ag_set_favicon + subroutine set_window_title(self, text) use appgraphics, only: setwindowtitle implicit none @@ -248,8 +267,18 @@ contains type(appgraphics_renderer)::self character(*), intent(in)::text - if(len_trim(text) > 0) then - call setwindowtitle("LR-87 :: "//trim(text)) + if(len_trim(text) > 256) then + self%title_text = text(1:250)//"..." + else + self%title_text = text + end if + + if(len_trim(self%title_text) > 0) then + if(len_trim(self%favicon) > 0) then + call setwindowtitle("LR-87 :: "//self%favicon//" "//trim(self%title_text)) + else + call setwindowtitle("LR-87 :: "//trim(self%title_text)) + end if else call setwindowtitle("LR-87") end if @@ -283,7 +312,7 @@ contains myexpand = .false. endif - self%address_bar_height = 24 + self%address_bar_height = scaledpi(24) ! Set up some address bar colors active_page = getactivepage() @@ -293,60 +322,69 @@ contains call setfillstyle(SOLID_FILL, LIGHTGRAY) call setbkcolor(LIGHTGRAY) call setcolor(BLACK) - call settextstyle(SYMBOLS_FONT, HORIZ_DIR, 14) + call settextstyle(SANS_SERIF_FONT, HORIZ_DIR, scaledpi(18)) end do call setactivepage(active_page) ! Draw the buttons first x = 5 if(self%back_button_id < 0) then - self%back_button_id = createbutton(x, 2, 40, 20, CHAR(231), back_button_callback) + self%back_button_id = createbutton(x, 2, scaledpi(40), scaledpi(20), & + ACHAR(240)//ACHAR(159)//ACHAR(161)//ACHAR(184), & + back_button_callback) else - call setbuttonposition(self%back_button_id, x, 2, 40, 20) + call setbuttonposition(self%back_button_id, x, 2, scaledpi(40), scaledpi(20)) end if - x = x + 50 + x = x + scaledpi(50) label_x = x - call settextstyle(WINDOWS_FONT, HORIZ_DIR, 12) + call settextstyle(WINDOWS_FONT, HORIZ_DIR, scaledpi(14)) x = x + textwidth("Address:") + 5 address_width = getmaxx()/2 - textwidth("Address:") - 5 + if(self%address_id < 0) then - self%address_id = createtextbox(x, 2, address_width, 20) + self%address_id = createtextbox(x, 2, address_width, scaledpi(20)) call settextboxentercallback(self%address_id, go_button_callback) else - call settextboxposition(self%address_id, x, 2, address_width, 20) + call settextboxposition(self%address_id, x, 2, address_width, scaledpi(20)) end if ! Clears any drawing operations for controls quickly ignored = switch_to_thread() - call settextstyle(SYMBOLS_FONT, HORIZ_DIR, 14) + call settextstyle(SANS_SERIF_FONT, HORIZ_DIR, scaledpi(18)) x = x + 10 + address_width if(self%go_button_id < 0) then - self%go_button_id = createbutton(x, 2, 40, 20, CHAR(232), go_button_callback) + self%go_button_id = createbutton(x, 2, scaledpi(40), scaledpi(20), & + ACHAR(240)//ACHAR(159)//ACHAR(161)//ACHAR(186), & + go_button_callback) else - call setbuttonposition(self%go_button_id, x, 2, 40, 20) + call setbuttonposition(self%go_button_id, x, 2, scaledpi(40), scaledpi(20)) end if - call settextstyle(SYMBOLS_FONT, HORIZ_DIR, 18) + call settextstyle(SANS_SERIF_FONT, HORIZ_DIR, scaledpi(20)) ! If we're not expanding, just draw these buttons in order if(.not. myexpand) then - x = getmaxx() - 95 + x = getmaxx() - scaledpi(95) if(self%fave_button_id < 0) then - self%fave_button_id = createbutton(x, 2, 40, 20, CHAR(171), fave_button_callback) + self%fave_button_id = createbutton(x, 2, scaledpi(40), scaledpi(20), & + ACHAR(226)//ACHAR(152)//ACHAR(133), & + fave_button_callback) else - call setbuttonposition(self%fave_button_id, x, 2, 40, 20) + call setbuttonposition(self%fave_button_id, x, 2, scaledpi(40), scaledpi(20)) end if - x = x + 50 + x = x + scaledpi(50) if(self%internal_button_id < 0) then - self%internal_button_id = createbutton(x, 2, 40, 20, CHAR(62), internal_button_callback) + self%internal_button_id = createbutton(x, 2, scaledpi(40), scaledpi(20), & + ACHAR(226)//ACHAR(156)//ACHAR(135), & + internal_button_callback) else - call setbuttonposition(self%internal_button_id, x, 2, 40, 20) + call setbuttonposition(self%internal_button_id, x, 2, scaledpi(40), scaledpi(20)) end if ! If expanding due to resize, we need to draw the rightmost first. @@ -354,11 +392,11 @@ contains ! check for that anymore else - x = getmaxx() - 45 - call setbuttonposition(self%internal_button_id, x, 2, 40, 20) + x = getmaxx() - scaledpi(45) + call setbuttonposition(self%internal_button_id, x, 2, scaledpi(40), scaledpi(20)) - x = x - 50 - call setbuttonposition(self%fave_button_id, x, 2, 40, 20) + x = x - scaledpi(50) + call setbuttonposition(self%fave_button_id, x, 2, scaledpi(40), scaledpi(20)) end if @@ -372,7 +410,7 @@ contains call setviewport(0, 0, getmaxx()+1, self%address_bar_height+1, .true.) call clearviewport() - call settextstyle(WINDOWS_FONT, HORIZ_DIR, 12) + call settextstyle(WINDOWS_FONT, HORIZ_DIR, scaledpi(14)) call outtextxy(label_x, 5, "Address:") call resetviewport() @@ -394,19 +432,18 @@ contains call resetviewport() call setviewport(0, getmaxy()-self%status_bar_height, getmaxx()+1, getmaxy()+1, .true.) active_page = getactivepage() - self%status_bar_height = 16 + self%status_bar_height = scaledpi(16) do i = 0, 1 call setactivepage(i) call setbkcolor(LIGHTGRAY) call setcolor(BLACK) - call settextstyle(WINDOWS_FONT, HORIZ_DIR, 12) + call settextstyle(WINDOWS_FONT, HORIZ_DIR, scaledpi(14)) call clearviewport() call outtextxy(5, 2, trim(text)) - end do call setactivepage(active_page) call resetviewport() @@ -459,14 +496,15 @@ contains class(appgraphics_renderer)::self - self%window_id = initwindow(default_width, default_height, "LR-87", & + call setapplicationdpiaware() + + self%window_id = initwindow(scaledpi(default_width), scaledpi(default_height), "LR-87", & DEFAULT_POSITION, DEFAULT_POSITION, & .TRUE., .FALSE.) call setwindowsmallicon(1) call setwindowlargeicon(1) - call setwindowclosecallback(window_closing_callback) call registermousehandler(MOUSE_LB_UP, mouse_button_callback) call registermousehandler(MOUSE_MOVE, mouse_move_callback) @@ -493,7 +531,7 @@ contains self%link_color = BLUE self%max_width = compute_max_width(self) - self%font_size = default_font_size + self%font_size = scaledpi(default_font_size) self%default_font = SERIF_FONT self%line_spacing = 2 self%left_border = 15 @@ -556,14 +594,44 @@ contains end subroutine ag_prepare_for_layout + function ag_rendering_height_available(self) + use appgraphics, only: getmaxy + implicit none + + class(appgraphics_renderer), intent(in)::self + integer::ag_rendering_height_available + + ag_rendering_height_available = getmaxy() - self%status_bar_height - & + self%address_bar_height - 1 + + end function ag_rendering_height_available + + function ag_scroll_max_value(self) + implicit none + + class(appgraphics_renderer), intent(in)::self + integer::ag_scroll_max_value + + ag_scroll_max_value = max(0, self%page_height - self%rendering_height_available()) + + end function ag_scroll_max_value + subroutine ag_layout_complete(self) use appgraphics, only: swapbuffers implicit none class(appgraphics_renderer)::self + integer::nPages call swapbuffers() + if(self%page_height == 0) then + self%page_height = self%y + self%initial_y + nPages = self%rendering_height_available() + call setscrollrange(self%scroll_id, 0, self%scroll_max_value()+nPages) + call setscrollpagesize(self%scroll_id, nPages) + end if + end subroutine ag_layout_complete subroutine ag_new_page(self) @@ -573,11 +641,13 @@ contains self%y = 0 scroll_position = 0 + wheel_movement = 0 call setscrollposition(self%scroll_id, 0) ag_render_event = ag_render_event_none self%title_guessed = .false. + self%page_height = 0 end subroutine ag_new_page @@ -630,7 +700,7 @@ contains ag_text_width = 0 end if - ag_text_width = ag_text_width + self%left_border + self%right_border + !ag_text_width = ag_text_width + self%left_border + self%right_border if(present(text_type)) then if(text_type == proportional_type_list_item) then @@ -701,7 +771,7 @@ contains case (proportional_type_heading_1) ! The first level 1 heading can be guessed as the page title if(.not. self%title_guessed) then - + call set_window_title(self, text) self%title_guessed = .true. @@ -769,7 +839,7 @@ contains text_width = ag_link_width(self, trim(text)) ! Need to remove indents, borders, etc. - text_width = text_width - self%left_border - self%right_border + !text_width = text_width - self%left_border - self%right_border call line(self%left_border, self%y + 7*self%font_size/8, & self%left_border + text_width, self%y + 7*self%font_size/8) @@ -803,7 +873,7 @@ contains ag_preformatted_width = 0 end if - ag_preformatted_width = ag_preformatted_width + self%left_border + self%right_border + !ag_preformatted_width = ag_preformatted_width + self%left_border + self%right_border end function ag_preformatted_width @@ -897,9 +967,6 @@ contains logical::expanding_horizontally - ! For scrolling - integer::doclength - ! Resize can be laggy, so ensure it actually was finalized if(self%max_width /= compute_max_width(self)) then ag_render_event = ag_render_event_resize @@ -942,15 +1009,25 @@ contains call clearmouseclick(MOUSE_MOVE) case(ag_render_event_wheel) + scroll_position = scroll_position - & + (wheel_movement/120)* & + self%text_height("Scrolled Example", proportional_type_normal) + + ! Limit + scroll_position = max(0, scroll_position) + scroll_position = min(self%scroll_max_value(), & + scroll_position) + call setscrollposition(self%scroll_id, scroll_position) - doclength = self%y - self%initial_y - self%y = (doclength * scroll_position) / (-100) + self%y = -1*scroll_position ag_action = render_action_layout + + wheel_movement = 0 case(ag_render_event_scroll) - doclength = self%y - self%initial_y - self%y = (doclength * scroll_position) / (-100) + + self%y = -1*scroll_position ag_action = render_action_layout case(ag_render_event_resize) diff --git a/dumb_render.f90 b/dumb_render.f90 index 64a3c40..31e7b9c 100644 --- a/dumb_render.f90 +++ b/dumb_render.f90 @@ -37,6 +37,8 @@ implicit none character(len=1024), dimension(height)::link_urls integer::first_line + + logical::is_utf8 contains @@ -76,14 +78,41 @@ implicit none procedure :: report_displayed_page => dumb_displayed_page + procedure :: prompt_user + procedure :: prompt_user_more + end type dumb_renderer contains + function len_trim_utf8(s) result(r) + use iso_c_binding + implicit none + + character(*), intent(in)::s + integer::r + + integer::cs + type(c_ptr)::cstr + + integer::i + + r = 0 + do i = 1,len_trim(s) + cs = ichar(s(i:i)) + if(iand(cs, 192) /= 128) then + r = r + 1 + end if + end do + + end function len_trim_utf8 + subroutine dumb_initialize(self) implicit none class(dumb_renderer)::self + character(len=32)::lang + integer::iostatus self%max_width = width self%y = 0 @@ -92,6 +121,15 @@ contains self%link_index = 0 self%link_urls = " " + self%is_utf8 = .FALSE. + call get_environment_variable("LANG", lang, status=iostatus) + if(iostatus == 0) then + self%is_utf8 = (index(lang, "utf-8") > 0 .or. & + index(lang, "UTF-8") > 0 .or. & + index(lang, "utf8") > 0 .or. & + index(lang, "UTF8") > 0) + end if + end subroutine dumb_initialize subroutine dumb_new_page(self) @@ -145,7 +183,11 @@ contains integer, intent(in), optional::text_type integer::dumb_text_width - dumb_text_width = len_trim(text) + if(self%is_utf8) then + dumb_text_width = len_trim_utf8(text) + else + dumb_text_width = len_trim(text) + end if if(present(text_type)) then if(text_type == proportional_type_list_item) then @@ -182,7 +224,11 @@ contains character(*), intent(in)::text integer::dumb_simple_width - dumb_simple_width = len_trim(text) + if(self%is_utf8) then + dumb_simple_width = len_trim_utf8(text) + else + dumb_simple_width = len_trim(text) + end if end function dumb_simple_width @@ -215,17 +261,34 @@ contains class(dumb_renderer)::self character(*), intent(in)::text - integer::limit_x - character(5)::formatting + integer::limit_x, w + character(6)::formatting + + if(self%is_utf8) then + w = len_trim_utf8(text) + else + w = len_trim(text) + end if + limit_x = len_trim(text) + + do while(w > self%max_width) + limit_x = limit_x - 1 + if(self%is_utf8) then + w = len_trim_utf8(text(1:limit_x)) + else + w = limit_x + end if + end do - limit_x = min(len_trim(text), self%max_width) if(limit_x == 0) then write(*,*) " " else if(limit_x < 10) then write(formatting, '(A2, I1, A1)') '(A', limit_x, ')' - else + else if(limit_x < 100) then write(formatting, '(A2, I2, A1)') '(A', limit_x, ')' + else + write(formatting, '(A2, I3, A1)') '(A', limit_x, ')' end if write(*, formatting) text(1:limit_x) @@ -389,22 +452,36 @@ contains end subroutine dumb_ready_status - subroutine prompt_user(input) + subroutine prompt_user(self, input) implicit none + class(dumb_renderer)::self character(*), intent(out)::input + + if(len_trim(self%favicon) > 0 .and. self%is_utf8) then + write(*, '(A4)', advance='no') self%favicon + else + write(*, '(A3)', advance='no') "***" + end if - write(*, '(A67)', advance='no') "*** [A]/[Z] PgUp/Dn | [#] Link | [B] Back | [M] More | [Q] Quit => " + write(*, '(1X, A63)', advance='no') "[A]/[Z] PgUp/Dn | [#] Link | [B] Back | [M] More | [Q] Quit => " read(*, *) input end subroutine prompt_user - subroutine prompt_user_more(input) + subroutine prompt_user_more(self, input) implicit none + class(dumb_renderer)::self character(*), intent(out)::input - write(*, '(A60)', advance='no') "*** [U] URL | [!] Save/Remove Fave | [L] Menu | [Q] Quit => " + if(len_trim(self%favicon) > 0 .and. self%is_utf8) then + write(*, '(A4)', advance='no') self%favicon + else + write(*, '(A3)', advance='no') "***" + end if + + write(*, '(1X, A56)', advance='no') "[U] URL | [!] Save/Remove Fave | [L] Menu | [Q] Quit => " read(*, *) input end subroutine prompt_user_more @@ -421,9 +498,9 @@ contains character(256)::input integer::link_id, iostatus - call prompt_user(input) + call prompt_user(self, input) if(trim(input) == 'M' .or. trim(input) == 'm') then - call prompt_user_more(input) + call prompt_user_more(self, input) end if if(len_trim(input) == 0) then diff --git a/favicon.f90 b/favicon.f90 new file mode 100644 index 0000000..b398426 --- /dev/null +++ b/favicon.f90 @@ -0,0 +1,114 @@ +module favicon +implicit none + + private + + public::get_favicon + +contains + + + subroutine get_server_favicon_file(server, filename) + use platform, only: dir_sep, get_settings_directory + implicit none + + character(*), intent(in)::server + character(*), intent(out)::filename + + character(len=256)::dir + + call get_settings_directory(dir) + filename = trim(dir)//dir_sep//trim(server)//".favicon.txt" + + end subroutine get_server_favicon_file + + function get_favicon_from_cache(server) result(f) + implicit none + + character(len=4)::f + character(*), intent(in)::server + character(len=384)::filename + + integer::unumber, iostatus + + call get_server_favicon_file(server, filename) + + f = ' ' + + open(newunit=unumber, file=filename, & + action="read", iostat=iostatus) + + if(iostatus == 0) then + ! second line + read(unumber, '(A)') f + if(trim(f) /= "NA") then + read(unumber, '(A)') f + end if + close(unumber) + end if + + end function get_favicon_from_cache + + function request_favicon(server) result(f) + use gemini_protocol, only: request_url, is_failure_code + implicit none + + character(len=4)::f + character(*), intent(in)::server + character(len=1024)::url + character(len=128)::return_type + + character(len=384)::cache_filename + + integer::return_code, unum, ios + + call get_server_favicon_file(server, cache_filename) + open(newunit=unum, file=cache_filename, action="write", iostat=ios) + if(ios == 0) then + + ! Per gemini://mozz.us/files/rfc_gemini_favicon.gmi + url = "gemini://"//trim(server)//"/favicon.txt" + + return_code = request_url(url, unum, return_type) + close(unum) + + if(is_failure_code(return_code)) then + open(newunit=unum, file=cache_filename, action="write", iostat=ios) + write(unum, '(A2,2X)') "NA" + close(unum) + end if + + f = get_favicon_from_cache(server) + + else + + f = ' ' + + end if + + end function request_favicon + + function get_favicon(server) result(f) + implicit none + + character(len=4)::f + character(*), intent(in)::server + + + + + f = get_favicon_from_cache(server) + + ! Blank - request it + if(len_trim(f) == 0) then + f = request_favicon(server) + end if + + ! NA - server said no at some point + if(trim(f) == "NA") then + f = ' ' + end if + + end function get_favicon + +end module favicon @@ -128,7 +128,7 @@ contains logical, intent(inout)::preformatted_on integer::line_length - + if(file_type == file_type_plain_text) then single_line%line_type = line_type_preformatted diff --git a/gemini-windows.prj b/gemini-windows.prj index f84b768..967d11c 100644 --- a/gemini-windows.prj +++ b/gemini-windows.prj @@ -61,6 +61,9 @@ "filename":".\\escape.f90", "enabled":"1" },{ + "filename":".\\favicon.f90", + "enabled":"1" + },{ "filename":".\\favorites.f90", "enabled":"1" },{ @@ -125,6 +128,7 @@ "Fortran Options":{ "Use C Preprocessor":"false", "Runtime Diagnostics":"false", + "Floating Point Exception Trap":0, "Cray Pointers":"false", "Enable Coarrays":"false", "Enable OpenMP":"false", @@ -134,24 +138,23 @@ "Code Generation Options":{ "CPU Specific":"false", "Processor":"generic", - "Aggressive Loops":"true", + "Aggressive Loops":"false", "Debugging":"false", "Optimization Mode":2, - "Floating Point Trap":"false", "Profiling":"false" }, "Build Dependencies":1, "Launch Options":{ - "Build Before Launch":"true", "Working Directory":"", "Launch Using MPI":"false", "Keep Console":"true", - "Executable":"", + "External Console":"false", "Command Line Arguments":"", - "External Console":"false" + "Build Before Launch":"true" }, "Build Options":{ - "Makefile":"Makefile", + "Auto Management":"true", + "Makefile":"Makefile.gemini-windows", "Auto Makefile":"true" }, "Linker Options":{ @@ -35,6 +35,9 @@ "filename":"escape.f90", "enabled":"1" },{ + "filename":"favicon.f90", + "enabled":"1" + },{ "filename":"favorites.f90", "enabled":"1" },{ @@ -102,6 +105,7 @@ "Fortran Options":{ "Use C Preprocessor":"false", "Runtime Diagnostics":"false", + "Floating Point Exception Trap":0, "Cray Pointers":"false", "Enable Coarrays":"false", "Enable OpenMP":"false", @@ -114,18 +118,16 @@ "Aggressive Loops":"false", "Debugging":"true", "Optimization Mode":0, - "Floating Point Trap":"false", "Profiling":"false" }, "Build Dependencies":1, "Launch Options":{ - "Build Before Launch":"true", "Working Directory":"", "Launch Using MPI":"false", "Keep Console":"true", - "Executable":"", + "External Console":"false", "Command Line Arguments":"", - "External Console":"false" + "Build Before Launch":"true" }, "Build Options":{ "Makefile":"Makefile", diff --git a/internal.F90 b/internal.F90 index 5ef2c5d..2a899ff 100644 --- a/internal.F90 +++ b/internal.F90 @@ -144,7 +144,7 @@ contains call write_header(unit_number, "About") call write_no_space(unit_number, "## LR-87 - A Gemini Browser Written in Fortran") - call write_no_space(unit_number, "Copyright 2020 Jeffrey Armstrong") + call write_no_space(unit_number, "Copyright 2020, 2021, 2022 Jeffrey Armstrong") call write_no_space(unit_number, "=> http://git.rainbow-100.com/cgit.cgi/LR-87/about/ LR-87 Web Home Page") call write_no_space(unit_number, "=> gemini://rainbow-100.com/software/lr87.gmi LR-87 Gemini Home Page") @@ -254,7 +254,10 @@ contains write(unit_number, *) - call write_no_space(unit_number, "=> gemini://gus.guru/ Gemini Universal Search - search all sites in Gemini") + call write_no_space(unit_number, "=> gemini://medusae.space/ medusae.space Directory - "//& + "a categorized directory of Gemini sites") + + call write_no_space(unit_number, "=> gemini://geminispace.info/ Gemini Search Engine - search all sites in Gemini") call write_no_space(unit_number, "=> gemini://rawtext.club:1965/~sloum/spacewalk.gmi Spacewalk - "// & "a list of recently updated Gemini sites") @@ -55,8 +55,17 @@ contains call rendering_engine%prepare_for_layout() do while(laying_out) - if(walker%line_type == line_type_text .and. .not. associated(walker%breaks)) then - walker%breaks => calculate_wrapping(rendering_engine, walker%text) + + if(.not. associated(walker%breaks)) then + select case (walker%line_type) + + case (line_type_text) + walker%breaks => calculate_wrapping(rendering_engine, walker%text) + + case (line_type_preformatted) + walker%breaks => calculate_stop(rendering_engine, walker%text) + + end select end if select case (walker%line_type) @@ -65,7 +74,7 @@ contains call render_proportional(rendering_engine, walker%text, walker%breaks) case (line_type_preformatted) - call render_preformatted(rendering_engine, walker%text) + call render_preformatted(rendering_engine, walker%text, walker%breaks(1)) case (line_type_link) call render_link(rendering_engine, walker%text) @@ -26,6 +26,7 @@ use request #ifdef WINDOWS_GUI use ag_render, only: appgraphics_renderer use ag_binary, only: appgraphics_binary_handler +use appgraphics, only: setapplicationdpiaware #else !use sdl_render use dumb_render @@ -47,11 +48,14 @@ use wsa_network, only: windows_network_startup => startup use favorite_handling +use favicon + implicit none character(256)::initial_site character(1024)::current_url, desired_url, input - type(connection)::conn + + character(1024)::server #ifdef WINDOWS_GUI type(appgraphics_renderer)::r @@ -79,6 +83,10 @@ implicit none #ifdef WINDOWS call windows_network_startup() #endif + +#ifdef WINDOWS_GUI + !call setapplicationdpiaware() +#endif if(command_argument_count() > 0) then @@ -101,7 +109,7 @@ implicit none #ifdef WINDOWS initial_site = "lr87://home" #else - initial_site = "gemini://gus.guru/" + initial_site = "gemini://medusae.space/" #endif end if @@ -119,7 +127,7 @@ implicit none current_url = " " first_line => null() - open(unit=io, form="formatted", status="scratch", access='stream') + open(unit=io, form="formatted", status='scratch', access='stream') do while(running) @@ -146,6 +154,10 @@ implicit none if(.not. loaded) then + call check_slashless_url(desired_url) + + call r%clear_favicon() + call r%report_status("Requesting "//trim(desired_url)) return_code = request_url(desired_url, io, return_type, bh) @@ -175,6 +187,9 @@ implicit none desired_url = " " locations_visited => add_location(locations_visited, current_url) + + call get_server_from_url(current_url, server) + call r%set_favicon(get_favicon(server)) if(r%type_supported(return_type)) then @@ -256,6 +271,8 @@ implicit none call handle_relative_url(desired_url, input) end if + call r%clear_favicon() + loaded = .false. case (render_action_favorite) @@ -370,5 +387,28 @@ contains end if end subroutine save_favorites + + ! Checks to see if the url is just a server name with no + ! trailing slash, and fixes it + subroutine check_slashless_url(url) + implicit none + + character(*), intent(inout)::url + integer::i, slash_count + + slash_count = 0 + if(index(url, "://") /= 0) then + do i = 1, len_trim(url) + if(url(i:i) == '/') then + slash_count = slash_count + 1 + end if + end do + + if(slash_count == 2) then + url(i:i) = "/" + end if + end if + + end subroutine check_slashless_url end program gemini diff --git a/makefile.gnu b/makefile.gnu index 25f051e..4994bdd 100644 --- a/makefile.gnu +++ b/makefile.gnu @@ -74,11 +74,16 @@ build/history.o: history.f90 | modules build @$(FC) -c -o "build/history.o" $(FFLAGS) "history.f90" modules/history.mod : | modules build/history.o -build/internal.o: internal.f90 modules/favorite_handling.mod modules/file_handling.mod | modules build - @echo Compiling internal.f90 - @$(FC) -c -o "build/internal.o" $(FFLAGS) "internal.f90" +build/internal.o: internal.F90 modules/favorite_handling.mod modules/file_handling.mod | modules build + @echo Compiling internal.F90 + @$(FC) -c -o "build/internal.o" $(FFLAGS) "internal.F90" modules/internal_links.mod : | modules build/internal.o +build/favicon.o: favicon.f90 modules/platform.mod modules/gemini_protocol.mod + @echo Compiling favicon.f90 + @$(FC) -c -o "build/favicon.o" $(FFLAGS) "favicon.f90" +modules/favicon.mod: | build/favicon.o + build/jessl.o: jessl.f90 | modules build @echo Compiling jessl.f90 @$(FC) -c -o "build/jessl.o" $(FFLAGS) "jessl.f90" @@ -89,7 +94,7 @@ build/layout.o: layout.f90 modules/render.mod | modules build @$(FC) -c -o "build/layout.o" $(FFLAGS) "layout.f90" modules/layout.mod : | modules build/layout.o -build/main.o: main.F90 modules/request.mod modules/dumb_render.mod modules/dumb_binary.mod modules/render.mod modules/gemini_protocol.mod modules/layout.mod modules/file_handling.mod modules/history.mod modules/internal_links.mod modules/favorite_handling.mod modules/platform.mod | modules build +build/main.o: main.F90 modules/favicon.mod modules/request.mod modules/dumb_render.mod modules/dumb_binary.mod modules/render.mod modules/gemini_protocol.mod modules/layout.mod modules/file_handling.mod modules/history.mod modules/internal_links.mod modules/favorite_handling.mod modules/platform.mod | modules build @echo Compiling main.F90 @$(FC) -c -o "build/main.o" $(FFLAGS) "main.F90" @@ -127,6 +132,8 @@ clean: @$(RM) "build/dumb_render.o" "modules/dumb_render.mod" "modules/dumb_render.smod" @echo Deleting build/escape.o and related files @$(RM) "build/escape.o" "modules/escaper.mod" "modules/escaper.smod" + @echo Deleting build/favicon.o and related files + @$(RM) "build/favicon.o" "modules/favicon.mod" @echo Deleting build/favorites.o and related files @$(RM) "build/favorites.o" "modules/favorite_handling.mod" "modules/favorite_handling.smod" @echo Deleting build/files.o and related files @@ -164,9 +171,9 @@ clean: @echo Deleting lr87 @$(RM) "lr87" -lr87: build/binary.o build/dumb_binary.o build/dumb_render.o build/escape.o build/favorites.o build/files.o build/history.o build/internal.o build/jessl.o build/layout.o build/main.o build/network.o build/platform.o build/protocol.o build/render.o build/request.o +lr87: build/binary.o build/dumb_binary.o build/dumb_render.o build/escape.o build/favicon.o build/favorites.o build/files.o build/history.o build/internal.o build/jessl.o build/layout.o build/main.o build/network.o build/platform.o build/protocol.o build/render.o build/request.o @echo Generating lr87 - @$(FC) -o "lr87" build/binary.o build/dumb_binary.o build/dumb_render.o build/escape.o build/favorites.o build/files.o build/history.o build/internal.o build/jessl.o build/layout.o build/main.o build/network.o build/platform.o build/protocol.o build/render.o build/request.o $(LDIR) $(PRJ_LFLAGS) + @$(FC) -o "lr87" build/binary.o build/dumb_binary.o build/dumb_render.o build/escape.o build/favicon.o build/favorites.o build/files.o build/history.o build/internal.o build/jessl.o build/layout.o build/main.o build/network.o build/platform.o build/protocol.o build/render.o build/request.o $(LDIR) $(PRJ_LFLAGS) all: lr87 diff --git a/platform.F90 b/platform.F90 index 73200d2..a04b902 100644 --- a/platform.F90 +++ b/platform.F90 @@ -30,6 +30,8 @@ implicit none #endif character(*), parameter::favorites_file = "favorites.gmi" + + logical::first_pass_makedir = .false. contains @@ -38,6 +40,7 @@ contains implicit none character(*), intent(in)::dir + character(256)::cmd #ifdef WINDOWS character(kind=c_char, len=:), allocatable, target::passdir @@ -113,7 +116,10 @@ contains #endif ! Harmless - call make_directory(dir) + if(.not. first_pass_makedir) then + call make_directory(dir) + first_pass_makedir = .true. + end if end subroutine get_settings_directory diff --git a/protocol.f90 b/protocol.f90 index 613ca9f..d06d901 100644 --- a/protocol.f90 +++ b/protocol.f90 @@ -114,7 +114,7 @@ contains character(*), intent(inout)::url integer, intent(in)::unit_number character(*), intent(out)::return_type - class(binary_handler)::bh + class(binary_handler), optional::bh character(*), intent(in), optional::server_name integer::port @@ -144,6 +144,7 @@ contains server = server_name port = gemini_default_port else + allocate(character(len=len_trim(url)) :: server) call get_server_from_url(url, server, port) if(port < 0) then port = gemini_default_port @@ -185,8 +186,12 @@ contains call get_mimetype(response_line, return_type) binary_file = is_binary_file(return_type) - if(binary_file) then - binary_unit = bh%handle_binary(return_type, trim(url), binary_status) + if(binary_file ) then + if(present(bh)) then + binary_unit = bh%handle_binary(return_type, trim(url), binary_status) + else + binary_status = binary_ignore + end if end if else @@ -48,6 +48,7 @@ implicit none integer::y integer::max_width + character(4)::favicon contains @@ -55,6 +56,8 @@ implicit none procedure::type_supported procedure::status_ready procedure::report_unsupported_protocol + procedure::set_favicon + procedure::clear_favicon procedure(initialize), deferred::initialize procedure(prepare_for_layout), deferred::prepare_for_layout @@ -267,6 +270,25 @@ contains end function report_unsupported_protocol + subroutine set_favicon(self, f) + implicit none + + class(renderer)::self + character(*), intent(in)::f + + self%favicon = f + + end subroutine set_favicon + + subroutine clear_favicon(self) + implicit none + + class(renderer)::self + + self%favicon = " " + + end subroutine clear_favicon + function width_of_line(r, text, startpos, endpos, proportional_type) implicit none @@ -377,7 +399,24 @@ contains end if end function get_start_position_and_type + + function calculate_stop(r, text) result(breaks) + implicit none + + class(renderer)::r + character(*), intent(in)::text + integer, dimension(:), pointer::breaks + + allocate(breaks(1)) + + breaks(1) = len_trim(text) + + do while(r%preformatted_width(text(1:breaks(1))) > r%max_width) + breaks(1) = breaks(1) - 1 + end do + end function calculate_stop + function calculate_wrapping(r, text) result(breaks) implicit none @@ -402,7 +441,8 @@ contains startpos = get_start_position_and_type(text, proportional_type) endpos = wrap_line(r, text, startpos, proportional_type) - do while(endpos > startpos) + + do while(endpos >= startpos) ! Save this break break_count = break_count + 1 @@ -465,6 +505,7 @@ contains integer::startpos, endpos integer::proportional_type integer::break_index + integer::h if(len_trim(text) == 0) then @@ -483,7 +524,7 @@ contains endpos = len_trim(text) end if - do while(endpos > startpos) + do while(endpos >= startpos) if(r%is_text_visible(text(startpos:endpos))) then call r%draw_proportional(text(startpos:endpos), & text_type=proportional_type) @@ -515,16 +556,25 @@ contains end subroutine render_proportional - subroutine render_preformatted(r, text) + subroutine render_preformatted(r, text, stoppoint) implicit none class(renderer)::r character(*)::text + integer, intent(in), optional::stoppoint + + integer::i + + if(present(stoppoint)) then + i = stoppoint + else + i = len_trim(text) + end if if(r%is_preformatted_visible(text)) then - call r%draw_preformatted(text) + call r%draw_preformatted(text(1:i)) end if - r%y = r%y + r%preformatted_height(text) + r%y = r%y + r%preformatted_height(text(1:i)) end subroutine render_preformatted diff --git a/request.f90 b/request.f90 index 139b368..e9043c0 100644 --- a/request.f90 +++ b/request.f90 @@ -173,13 +173,14 @@ contains implicit none character(*), intent(in)::url - character(:), allocatable, intent(out)::server - integer, intent(out)::port + character(*), intent(out)::server + integer, intent(out), optional::port integer::start_server, end_server, length integer::start_port, iostatus + integer::myport - port = -1 + myport = -1 start_server = index(url, "://") if(start_server > 0) then @@ -194,7 +195,6 @@ contains end if length = end_server - start_server + 1 - allocate(character(len=length) :: server) server = url(start_server:end_server) end if @@ -203,14 +203,18 @@ contains start_port = index(server, ":") if(start_port > 0) then - read(server(start_port+1:len_trim(server)), *, iostat=iostatus) port + read(server(start_port+1:len_trim(server)), *, iostat=iostatus) myport if(iostatus /= 0) then - port = -1 + myport = -1 end if server = server(1:start_port-1) end if + + if(present(port)) then + port = myport + end if end subroutine get_server_from_url diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 86da76c..9882b83 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,6 +1,6 @@ name: lr87 base: core18 -version: '0.11' +version: '0.12' summary: LR-87 is a overly simple dumb-terminal gemini client description: | LR-87 is a simple implementation of a client for launching into the world of the |