astropanel 2.5 → 2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/astropanel +139 -39
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab87ddd7784d3b47b1a807f39f4a1116ef5498da9e1abed7aa83c77daae7e5a4
|
4
|
+
data.tar.gz: d2c5f209b93b9cd369ee056aa9d41e8b118f0c6137e99797cf58656c15bb8aab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18c52254d3c68c8f03797639d5d9cc8546a5824a8db17200fdd8345a3c53256232eec6058ff7d1fec4f55a2305dfac89c0db37f4f82eedf9698b8e978fb286b0
|
7
|
+
data.tar.gz: 9c7cb190ce0502763f5ef4ff16dd08ea11f6621f77b39555deb698f7d172b2ac6c4c647b937676a2208b8c630ed64b47f19c8ee5ceaab52b0dcc9cf900e569f0
|
data/bin/astropanel
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
# for any damages resulting from its use. Further, I am under no
|
15
15
|
# obligation to maintain or extend this software. It is provided
|
16
16
|
# on an 'as is' basis without any expressed or implied warranty.
|
17
|
-
# Version: 2.
|
17
|
+
# Version: 2.7: Image redraw fix for i3-wm workspace switching
|
18
18
|
|
19
19
|
# LOAD MODULES {{{1
|
20
20
|
require 'io/console'
|
@@ -33,6 +33,7 @@ include Rcurses::Cursor
|
|
33
33
|
# BASIC SETUP {{{1
|
34
34
|
CONFIG_FILE = File.join(Dir.home, '.ap.conf')
|
35
35
|
|
36
|
+
|
36
37
|
# CLASS EXTENSIONS {{{1
|
37
38
|
class Numeric # {{{2
|
38
39
|
def deg; self * Math::PI / 180; end
|
@@ -543,6 +544,34 @@ class AstroPanelApp
|
|
543
544
|
# allow override from ~/.ap.conf
|
544
545
|
@w3mimgdisplay ||= "/usr/lib/w3m/w3mimgdisplay"
|
545
546
|
@showimage = File.executable?(@w3mimgdisplay)
|
547
|
+
@current_image = nil
|
548
|
+
end
|
549
|
+
|
550
|
+
def check_image_redraw # {{{2
|
551
|
+
# Only check if image display is enabled
|
552
|
+
return unless @showimage
|
553
|
+
|
554
|
+
# Only check if we have an image currently displayed
|
555
|
+
return unless @current_image && File.exist?(@current_image)
|
556
|
+
|
557
|
+
begin
|
558
|
+
# Check if we're in the active window - if not, image overlay might be cleared
|
559
|
+
active_window = `xdotool getactivewindow 2>/dev/null`.chomp
|
560
|
+
return if active_window.empty?
|
561
|
+
|
562
|
+
# Check if this terminal window is the active one
|
563
|
+
current_window = `xdotool getwindowfocus 2>/dev/null`.chomp
|
564
|
+
|
565
|
+
# Only redraw if we detect a focus change (simple heuristic)
|
566
|
+
if @last_active_window && @last_active_window != active_window && current_window == active_window
|
567
|
+
# Window focus changed and we're now active - redraw image
|
568
|
+
show_image(@current_image)
|
569
|
+
end
|
570
|
+
|
571
|
+
@last_active_window = active_window
|
572
|
+
rescue
|
573
|
+
# Silently fail - we don't want focus checking to break anything
|
574
|
+
end
|
546
575
|
end
|
547
576
|
|
548
577
|
def show_image(file=nil) # {{{2
|
@@ -580,6 +609,12 @@ class AstroPanelApp
|
|
580
609
|
# Clear image area
|
581
610
|
`echo "6;#{px};#{py};#{max_w};#{max_h};\n4;\n3;" | #{@w3mimgdisplay} 2>/dev/null`
|
582
611
|
`echo "0;1;#{px};#{py};#{iw};#{ih};;;;;\"#{file}\"\n4;\n3;" | #{@w3mimgdisplay} 2>/dev/null`
|
612
|
+
|
613
|
+
# Track the current image
|
614
|
+
@current_image = file
|
615
|
+
else
|
616
|
+
# Clear image if no file or file doesn't exist
|
617
|
+
@current_image = nil
|
583
618
|
end
|
584
619
|
end
|
585
620
|
rescue Timeout::Error
|
@@ -659,13 +694,22 @@ class AstroPanelApp
|
|
659
694
|
@left = Pane.new( 2, 3, 70, rows - 3, 248, 232)
|
660
695
|
@main = Pane.new( 74, 3, cols - 74, rows - 4, 255, 232)
|
661
696
|
@footer = Pane.new( 1, cols, cols, 1, 255, 24)
|
697
|
+
|
698
|
+
# Add update attributes for selective rendering
|
699
|
+
[@header, @titles, @left, @main, @footer].each do |pane|
|
700
|
+
pane.define_singleton_method(:update) { @update }
|
701
|
+
pane.define_singleton_method(:update=) { |val| @update = val }
|
702
|
+
pane.update = false
|
703
|
+
end
|
662
704
|
end
|
663
705
|
|
664
706
|
def run_loop # {{{2
|
665
707
|
fetch_all
|
666
708
|
show_image(@current_image) if @current_image
|
709
|
+
@last_render_state = nil
|
710
|
+
@left.update = @main.update = @header.update = @footer.update = true
|
667
711
|
loop do
|
668
|
-
|
712
|
+
render_selective
|
669
713
|
handle_input
|
670
714
|
end
|
671
715
|
ensure
|
@@ -705,8 +749,51 @@ class AstroPanelApp
|
|
705
749
|
COND_COLORS[level]
|
706
750
|
end
|
707
751
|
|
752
|
+
def current_render_state # {{{2
|
753
|
+
[@index, @weather.size, @time, @today].hash
|
754
|
+
end
|
755
|
+
|
756
|
+
def needs_render? # {{{2
|
757
|
+
current_state = current_render_state
|
758
|
+
state_changed = @last_render_state != current_state
|
759
|
+
|
760
|
+
# Also check if any panes need updating
|
761
|
+
panes_need_update = @left.update || @main.update || @header.update || @footer.update
|
762
|
+
|
763
|
+
needs_update = state_changed || panes_need_update
|
764
|
+
@last_render_state = current_state if state_changed
|
765
|
+
needs_update
|
766
|
+
end
|
767
|
+
|
768
|
+
def render_selective # {{{2
|
769
|
+
return unless needs_render?
|
770
|
+
|
771
|
+
if @header.update
|
772
|
+
update_header
|
773
|
+
@header.update = false
|
774
|
+
end
|
775
|
+
|
776
|
+
if @footer.update
|
777
|
+
update_footer
|
778
|
+
@footer.update = false
|
779
|
+
end
|
780
|
+
|
781
|
+
if @left.update
|
782
|
+
update_left
|
783
|
+
@left.update = false
|
784
|
+
end
|
785
|
+
|
786
|
+
if @main.update
|
787
|
+
update_main
|
788
|
+
@main.update = false
|
789
|
+
end
|
790
|
+
|
791
|
+
update_titles # Always update titles as they're lightweight
|
792
|
+
end
|
793
|
+
|
708
794
|
def draw_all # {{{2
|
709
|
-
|
795
|
+
@header.update = @footer.update = @left.update = @main.update = true
|
796
|
+
render_selective
|
710
797
|
end
|
711
798
|
|
712
799
|
def update_header # {{{2
|
@@ -760,10 +847,7 @@ class AstroPanelApp
|
|
760
847
|
@footer.say(cmds.ljust(cols))
|
761
848
|
end
|
762
849
|
|
763
|
-
|
764
|
-
# Redraw the left‐pane list of weather + visibility blocks,
|
765
|
-
# safely handling missing planet data and converting rise/set to integers
|
766
|
-
def update_left
|
850
|
+
def update_left # {{{2
|
767
851
|
date_o = ''
|
768
852
|
date_n = ''
|
769
853
|
|
@@ -898,40 +982,47 @@ class AstroPanelApp
|
|
898
982
|
|
899
983
|
def handle_input # {{{2
|
900
984
|
old_index = @index
|
901
|
-
|
985
|
+
key = getchr(1) # 1 second timeout like rtfm
|
986
|
+
|
987
|
+
unless key # If timeout occurred (no key pressed)
|
988
|
+
check_image_redraw # Check if image needs redraw after workspace switch
|
989
|
+
return
|
990
|
+
end
|
991
|
+
|
992
|
+
case key
|
902
993
|
when 'UP' then @index = (@index - 1) % @weather.size
|
903
994
|
when 'DOWN' then @index = (@index + 1) % @weather.size
|
904
995
|
when 'PgUP' then @index = [@index - @left.h, 0].max
|
905
996
|
when 'PgDOWN' then @index = [@index + @left.h, @weather.size - 1].min
|
906
997
|
when 'HOME' then @index = 0
|
907
998
|
when 'END' then @index = @weather.size - 1
|
908
|
-
when '?' then @main.say(HELP); getchr
|
909
|
-
when 'l' then @loc = @footer.ask('Loc? ', @loc)
|
910
|
-
when 'a' then @lat = @footer.ask('Lat? ', @lat.to_s).to_f
|
911
|
-
when 'o' then @lon = @footer.ask('Lon? ', @lon.to_s).to_f
|
912
|
-
when 'c' then @cloudlimit = @footer.ask('Maximum Cloud coverage? ', @cloudlimit.to_s).to_i
|
913
|
-
when 'h' then @humiditylimit = @footer.ask('Maximum Humidity? ', @humiditylimit.to_s).to_i
|
914
|
-
when 't' then @templimit = @footer.ask('Minimum Temperature? ', @templimit.to_s).to_i
|
915
|
-
when 'w' then @windlimit = @footer.ask('Maximum Wind? ', @windlimit.to_s).to_i
|
916
|
-
when 'b' then @bortle = @footer.ask('Bortle? ', @bortle.to_s).to_f
|
917
|
-
when 'e' then show_all_events
|
918
|
-
when 's' then starchart
|
999
|
+
when '?' then @main.say(HELP); getchr; @main.update = true
|
1000
|
+
when 'l' then @loc = @footer.ask('Loc? ', @loc); @header.update = @footer.update = true
|
1001
|
+
when 'a' then @lat = @footer.ask('Lat? ', @lat.to_s).to_f; @header.update = @footer.update = true
|
1002
|
+
when 'o' then @lon = @footer.ask('Lon? ', @lon.to_s).to_f; @header.update = @footer.update = true
|
1003
|
+
when 'c' then @cloudlimit = @footer.ask('Maximum Cloud coverage? ', @cloudlimit.to_s).to_i; @footer.update = true
|
1004
|
+
when 'h' then @humiditylimit = @footer.ask('Maximum Humidity? ', @humiditylimit.to_s).to_i; @footer.update = true
|
1005
|
+
when 't' then @templimit = @footer.ask('Minimum Temperature? ', @templimit.to_s).to_i; @footer.update = true
|
1006
|
+
when 'w' then @windlimit = @footer.ask('Maximum Wind? ', @windlimit.to_s).to_i; @footer.update = true
|
1007
|
+
when 'b' then @bortle = @footer.ask('Bortle? ', @bortle.to_s).to_f; @footer.update = true
|
1008
|
+
when 'e' then show_all_events; @main.update = true
|
1009
|
+
when 's' then starchart; @main.update = true
|
919
1010
|
when 'S' then system("xdg-open /tmp/starchart.jpg &")
|
920
|
-
when 'A' then apod
|
921
|
-
when 'r' then refresh_all
|
922
|
-
when 'R' then fetch_all
|
923
|
-
when 'W' then save_config; @footer.say("Config saved"); getchr
|
1011
|
+
when 'A' then apod; @main.update = true
|
1012
|
+
when 'r' then refresh_all; @header.update = @left.update = @main.update = @footer.update = true
|
1013
|
+
when 'R' then fetch_all; @header.update = @left.update = @main.update = @footer.update = true
|
1014
|
+
when 'W' then save_config; @footer.say("Config saved"); getchr; @footer.update = true
|
924
1015
|
when 'q' then exit
|
925
1016
|
end
|
926
1017
|
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
1018
|
+
# Only update panes if index changed (for navigation keys)
|
1019
|
+
if @index != old_index
|
1020
|
+
@left.update = @main.update = true
|
1021
|
+
end
|
931
1022
|
end
|
932
1023
|
|
933
|
-
|
934
|
-
|
1024
|
+
def get_weather # {{{2
|
1025
|
+
# Fetch weather from met.no, but never let network errors crash the app
|
935
1026
|
uri = URI("https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=#{@lat}&lon=#{@lon}")
|
936
1027
|
req = Net::HTTP::Get.new(uri)
|
937
1028
|
req['User-Agent'] = 'astropanel/1.0 g@isene.com'
|
@@ -1014,25 +1105,34 @@ class AstroPanelApp
|
|
1014
1105
|
end
|
1015
1106
|
|
1016
1107
|
def get_planets # {{{2
|
1108
|
+
# Build @planets entries for each unique date in @weather
|
1017
1109
|
@planets.clear
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1110
|
+
|
1111
|
+
# Collect exactly the dates we need (in first-seen order)
|
1112
|
+
dates = @weather.map { |w| w[:date] }.uniq
|
1113
|
+
|
1114
|
+
dates.each do |date|
|
1115
|
+
# Compute a fresh Ephemeris for that date
|
1116
|
+
ep = Ephemeris.new(date, @lat, @lon, @tz.to_i)
|
1117
|
+
|
1118
|
+
# Start with the printed table + moon phase info
|
1021
1119
|
entry = {
|
1022
1120
|
table: ep.print,
|
1023
|
-
mphase: ep.mphase,
|
1024
|
-
mph_s: ep.mph_s
|
1121
|
+
mphase: ep.mphase,
|
1122
|
+
mph_s: ep.mph_s
|
1025
1123
|
}
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1124
|
+
|
1125
|
+
# Add rise/set times for each body
|
1126
|
+
Ephemeris::BODY_ORDER.each do |body|
|
1127
|
+
arr = ep.send(body)
|
1128
|
+
entry[:"#{body}_rise"] = arr[5]
|
1129
|
+
entry[:"#{body}_set"] = arr[7]
|
1030
1130
|
end
|
1131
|
+
|
1031
1132
|
@planets[date] = entry
|
1032
1133
|
end
|
1033
1134
|
end
|
1034
1135
|
|
1035
|
-
|
1036
1136
|
def get_events # {{{2
|
1037
1137
|
@events.clear
|
1038
1138
|
uri = URI(
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: astropanel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '2.
|
4
|
+
version: '2.7'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geir Isene
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05
|
11
|
+
date: 2025-07-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rcurses
|
@@ -28,8 +28,8 @@ description: 'This program shows essential data in order to plan your observatio
|
|
28
28
|
9 days weather forecast, full ephemeris for the Sun, the Moon and all major planets,
|
29
29
|
complete with graphic representation of rise/set times, detailed info for each day
|
30
30
|
with important astronomical events, star chart displayed in the terminal and more.
|
31
|
-
New in 2.0: Full rewrite using rcurses (https://github.com/isene/rcurses). 2.
|
32
|
-
|
31
|
+
New in 2.0: Full rewrite using rcurses (https://github.com/isene/rcurses). 2.7:
|
32
|
+
Image redraw fix for i3-wm workspace switching.'
|
33
33
|
email: g@isene.com
|
34
34
|
executables:
|
35
35
|
- astropanel
|