Skip to content

Commit 9bc8cc6

Browse files
committed
Critical fixes based on code review
Address all CRITICAL issues identified by codex review: 1. Fix PID retrieval: Use proper getpid() via C interop instead of non-existent $PID env var 2. Fix Windows support: Use platform-specific temp directories (%TEMP% vs /tmp) 3. Fix OS detection: Parse uname -s output to distinguish Darwin from Linux 4. Fix macOS display detection: Check SSH_CONNECTION and assume GUI available 5. Add temp file cleanup helpers (cleanup_temp_file subroutine) 6. Better error messages: Show temp file path when viewer fails 7. Use get_temp_filename() with PID + system_clock for uniqueness 8. Handle Windows path separators (backslash vs forward slash) Remaining architecture concerns (filename==terminal, ASCII fallback) are acceptable for initial implementation and can be improved in future PRs.
1 parent 03bb80c commit 9bc8cc6

File tree

4 files changed

+167
-51
lines changed

4 files changed

+167
-51
lines changed

src/backends/raster/fortplot_png.f90

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,33 +73,26 @@ end function create_png_canvas
7373
! All drawing methods are inherited from raster_context
7474

7575
subroutine png_finalize(this, filename)
76-
use fortplot_system_viewer, only: launch_system_viewer, has_graphical_session
76+
use fortplot_system_viewer, only: launch_system_viewer, has_graphical_session, &
77+
get_temp_filename
7778
class(png_context), intent(inout) :: this
7879
character(len=*), intent(in) :: filename
7980
character(len=1024) :: temp_file
8081
logical :: viewer_success
81-
integer :: pid
8282

8383
if (trim(filename) == 'terminal') then
8484
if (has_graphical_session()) then
85-
call get_environment_variable('USER', temp_file)
86-
if (len_trim(temp_file) == 0) temp_file = 'user'
87-
call get_environment_variable('PID', temp_file)
88-
if (len_trim(temp_file) == 0) then
89-
pid = 0
90-
else
91-
read(temp_file, *) pid
92-
end if
93-
write(temp_file, '(A,I0,A)') '/tmp/fortplot_show_', pid, '.png'
94-
85+
call get_temp_filename('.png', temp_file)
9586
call write_png_file(temp_file, this%width, this%height, this%raster%image_data)
9687
call launch_system_viewer(temp_file, viewer_success)
9788
if (.not. viewer_success) then
98-
call log_error("Failed to launch PNG viewer")
89+
call log_error("Failed to launch PNG viewer for: " // trim(temp_file))
90+
call log_info("You can manually open: " // trim(temp_file))
9991
end if
10092
else
101-
call log_info("No graphical session detected, falling back to ASCII")
102-
call fallback_to_ascii(this)
93+
call log_info("No graphical session detected, cannot display PNG")
94+
call log_info("Use savefig('filename.png') to save to file or")
95+
call log_info("Use savefig('filename.txt') for ASCII rendering")
10396
end if
10497
else
10598
call write_png_file(filename, this%width, this%height, this%raster%image_data)

src/backends/vector/fortplot_pdf.f90

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -172,28 +172,22 @@ subroutine draw_pdf_text_wrapper(this, x, y, text)
172172
end subroutine draw_pdf_text_wrapper
173173

174174
subroutine write_pdf_file_facade(this, filename)
175-
use fortplot_system_viewer, only: launch_system_viewer, has_graphical_session
175+
use fortplot_system_viewer, only: launch_system_viewer, has_graphical_session, &
176+
get_temp_filename
176177
class(pdf_context), intent(inout) :: this
177178
character(len=*), intent(in) :: filename
178179
logical :: file_success
179-
character(len=1024) :: temp_file, actual_filename
180+
character(len=1024) :: actual_filename
180181
logical :: viewer_success
181-
integer :: pid
182182

183183
! Handle terminal display
184184
if (trim(filename) == 'terminal') then
185185
if (has_graphical_session()) then
186-
call get_environment_variable('USER', temp_file)
187-
if (len_trim(temp_file) == 0) temp_file = 'user'
188-
call get_environment_variable('PID', temp_file)
189-
if (len_trim(temp_file) == 0) then
190-
pid = 0
191-
else
192-
read(temp_file, *) pid
193-
end if
194-
write(actual_filename, '(A,I0,A)') '/tmp/fortplot_show_', pid, '.pdf'
186+
call get_temp_filename('.pdf', actual_filename)
195187
else
196188
call log_info("No graphical session detected, cannot display PDF")
189+
call log_info("Use savefig('filename.pdf') to save to file or")
190+
call log_info("Use savefig('filename.txt') for ASCII rendering")
197191
return
198192
end if
199193
else
@@ -229,7 +223,8 @@ subroutine write_pdf_file_facade(this, filename)
229223
if (trim(filename) == 'terminal' .and. has_graphical_session()) then
230224
call launch_system_viewer(actual_filename, viewer_success)
231225
if (.not. viewer_success) then
232-
call log_error("Failed to launch PDF viewer")
226+
call log_error("Failed to launch PDF viewer for: " // trim(actual_filename))
227+
call log_info("You can manually open: " // trim(actual_filename))
233228
end if
234229
end if
235230
end subroutine write_pdf_file_facade

src/system/fortplot_system_viewer.f90

Lines changed: 116 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,45 @@ module fortplot_system_viewer
22
!! System viewer launching for show() functionality
33
!! Handles platform-specific viewer launching and graphical session detection
44
use, intrinsic :: iso_fortran_env, only: wp => real64
5+
use, intrinsic :: iso_c_binding, only: c_int
56
use fortplot_logging, only: log_info, log_error
67
implicit none
78
private
89

910
public :: launch_system_viewer
1011
public :: has_graphical_session
12+
public :: get_temp_filename
13+
public :: cleanup_temp_file
14+
15+
interface
16+
function c_getpid() bind(C, name="getpid") result(pid)
17+
import :: c_int
18+
integer(c_int) :: pid
19+
end function c_getpid
20+
end interface
1121

1222
contains
1323

1424
function has_graphical_session() result(has_display)
1525
!! Check if a graphical session is available
16-
!! Returns .true. if X11, Wayland, or other display is available
26+
!! Returns .true. if X11, Wayland, macOS GUI, or Windows GUI available
1727
logical :: has_display
18-
character(len=256) :: display_var, wayland_var, session_type
28+
character(len=256) :: display_var, wayland_var, session_type, ssh_var
29+
character(len=32) :: os_type
1930

2031
has_display = .false.
32+
os_type = get_os_type()
33+
34+
if (trim(os_type) == 'windows') then
35+
has_display = .true.
36+
return
37+
end if
38+
39+
if (trim(os_type) == 'darwin') then
40+
call get_environment_variable('SSH_CONNECTION', ssh_var)
41+
has_display = len_trim(ssh_var) == 0
42+
return
43+
end if
2144

2245
call get_environment_variable('DISPLAY', display_var)
2346
if (len_trim(display_var) > 0) then
@@ -89,37 +112,107 @@ end function get_viewer_command
89112
function get_os_type() result(os_type)
90113
!! Detect operating system type
91114
character(len=32) :: os_type
92-
character(len=256) :: ostype_var
93-
integer :: stat
115+
character(len=256) :: ostype_var, uname_s
116+
integer :: stat, uname_unit, ios
117+
118+
call get_environment_variable('OS', ostype_var)
119+
if (index(ostype_var, 'Windows') > 0) then
120+
os_type = 'windows'
121+
return
122+
end if
123+
124+
call execute_command_line('uname -s > /tmp/fortplot_uname.txt', exitstat=stat)
125+
if (stat == 0) then
126+
open(newunit=uname_unit, file='/tmp/fortplot_uname.txt', status='old', iostat=ios)
127+
if (ios == 0) then
128+
read(uname_unit, '(A)', iostat=ios) uname_s
129+
close(uname_unit, status='delete')
130+
if (index(uname_s, 'Darwin') > 0) then
131+
os_type = 'darwin'
132+
return
133+
else if (index(uname_s, 'Linux') > 0) then
134+
os_type = 'linux'
135+
return
136+
end if
137+
end if
138+
end if
94139

95140
call get_environment_variable('OSTYPE', ostype_var)
96-
if (len_trim(ostype_var) > 0) then
97-
if (index(ostype_var, 'linux') > 0) then
98-
os_type = 'linux'
99-
return
100-
else if (index(ostype_var, 'darwin') > 0) then
101-
os_type = 'darwin'
141+
if (index(ostype_var, 'darwin') > 0 .or. index(ostype_var, 'Darwin') > 0) then
142+
os_type = 'darwin'
143+
else if (index(ostype_var, 'linux') > 0 .or. index(ostype_var, 'Linux') > 0) then
144+
os_type = 'linux'
145+
else if (index(ostype_var, 'win') > 0 .or. index(ostype_var, 'msys') > 0) then
146+
os_type = 'windows'
147+
else
148+
os_type = 'linux'
149+
end if
150+
end function get_os_type
151+
152+
function get_temp_dir() result(tempdir)
153+
!! Get platform-specific temporary directory
154+
character(len=256) :: tempdir
155+
character(len=256) :: temp_env
156+
character(len=32) :: os_type
157+
158+
os_type = get_os_type()
159+
160+
if (trim(os_type) == 'windows') then
161+
call get_environment_variable('TEMP', temp_env)
162+
if (len_trim(temp_env) > 0) then
163+
tempdir = trim(temp_env)
102164
return
103-
else if (index(ostype_var, 'win') > 0 .or. index(ostype_var, 'msys') > 0) then
104-
os_type = 'windows'
165+
end if
166+
call get_environment_variable('TMP', temp_env)
167+
if (len_trim(temp_env) > 0) then
168+
tempdir = trim(temp_env)
105169
return
106170
end if
171+
tempdir = 'C:\Temp'
172+
else
173+
tempdir = '/tmp'
107174
end if
175+
end function get_temp_dir
176+
177+
subroutine get_temp_filename(extension, filename)
178+
!! Generate unique temporary filename with proper platform handling
179+
character(len=*), intent(in) :: extension
180+
character(len=*), intent(out) :: filename
181+
character(len=256) :: tempdir
182+
character(len=1) :: sep
183+
character(len=32) :: os_type
184+
integer(c_int) :: pid
185+
integer :: clk_count, clk_rate, clk_max
108186

109-
call execute_command_line('uname', exitstat=stat)
110-
if (stat == 0) then
111-
os_type = 'linux'
112-
return
113-
end if
187+
tempdir = get_temp_dir()
188+
os_type = get_os_type()
189+
pid = c_getpid()
190+
call system_clock(clk_count, clk_rate, clk_max)
114191

115-
call get_environment_variable('OS', ostype_var)
116-
if (index(ostype_var, 'Windows') > 0) then
117-
os_type = 'windows'
118-
return
192+
if (trim(os_type) == 'windows') then
193+
sep = '\'
194+
else
195+
sep = '/'
119196
end if
120197

121-
os_type = 'linux'
122-
end function get_os_type
198+
write(filename, '(A,A,A,I0,A,I0,A)') trim(tempdir), sep, 'fortplot_show_', &
199+
int(pid), '_', clk_count, trim(extension)
200+
end subroutine get_temp_filename
201+
202+
subroutine cleanup_temp_file(filename)
203+
!! Remove temporary file (best effort, no error on failure)
204+
character(len=*), intent(in) :: filename
205+
integer :: stat
206+
logical :: exists
207+
208+
inquire(file=trim(filename), exist=exists)
209+
if (exists) then
210+
open(newunit=stat, file=trim(filename), status='old', iostat=stat)
211+
if (stat == 0) then
212+
close(stat, status='delete', iostat=stat)
213+
end if
214+
end if
215+
end subroutine cleanup_temp_file
123216

124217
function int_to_str(i) result(str)
125218
!! Convert integer to string

test_show.f90

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
program test_show
2+
use fortplot_figure, only: figure_t
3+
implicit none
4+
5+
type(figure_t) :: fig
6+
real :: x(100), y(100)
7+
integer :: i
8+
9+
! Generate simple test data
10+
do i = 1, 100
11+
x(i) = real(i) / 10.0
12+
y(i) = sin(x(i))
13+
end do
14+
15+
! Test PNG backend with terminal display
16+
print *, "Testing PNG backend show()..."
17+
call fig%init(backend='PNG')
18+
call fig%plot(x, y)
19+
call fig%show() ! Should launch viewer or log message
20+
21+
! Test PDF backend with terminal display
22+
print *, "Testing PDF backend show()..."
23+
call fig%init(backend='PDF')
24+
call fig%plot(x, y)
25+
call fig%show() ! Should launch viewer or log message
26+
27+
! Test ASCII backend with terminal display
28+
print *, "Testing ASCII backend show()..."
29+
call fig%init(backend='ASCII')
30+
call fig%plot(x, y)
31+
call fig%show() ! Should output to terminal
32+
33+
print *, "Tests complete"
34+
35+
end program test_show

0 commit comments

Comments
 (0)