aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md7
-rw-r--r--ag_render.f90175
-rw-r--r--dumb_render.f90101
-rw-r--r--favicon.f90114
-rw-r--r--files.f902
-rw-r--r--gemini-windows.prj15
-rw-r--r--gemini.prj10
-rw-r--r--internal.F907
-rw-r--r--layout.f9015
-rw-r--r--main.F9046
-rw-r--r--makefile.gnu19
-rw-r--r--platform.F908
-rw-r--r--protocol.f9011
-rw-r--r--render.f9060
-rw-r--r--request.f9016
-rw-r--r--snap/snapcraft.yaml2
16 files changed, 504 insertions, 104 deletions
diff --git a/README.md b/README.md
index 446cb7c..0f75fe1 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/files.f90 b/files.f90
index 1c76311..f405d00 100644
--- a/files.f90
+++ b/files.f90
@@ -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":{
diff --git a/gemini.prj b/gemini.prj
index b1cff20..6cf9a33 100644
--- a/gemini.prj
+++ b/gemini.prj
@@ -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")
diff --git a/layout.f90 b/layout.f90
index e28981d..68badef 100644
--- a/layout.f90
+++ b/layout.f90
@@ -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)
diff --git a/main.F90 b/main.F90
index 7d9206e..7903ca7 100644
--- a/main.F90
+++ b/main.F90
@@ -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
diff --git a/render.f90 b/render.f90
index 9027f83..7a65127 100644
--- a/render.f90
+++ b/render.f90
@@ -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