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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/astropanel +139 -39
  3. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba68a8b88ebc1705f759ebf2b93bb0bfc8f13a0fc3ee75b8223e0c0f46f7e0d5
4
- data.tar.gz: 7cd712f5bbc7951d13c6b6f1394984950bc732970ce593b21a2d78d1d94f4ad4
3
+ metadata.gz: ab87ddd7784d3b47b1a807f39f4a1116ef5498da9e1abed7aa83c77daae7e5a4
4
+ data.tar.gz: d2c5f209b93b9cd369ee056aa9d41e8b118f0c6137e99797cf58656c15bb8aab
5
5
  SHA512:
6
- metadata.gz: 072c8766c42c619a0999c8aa715bfc35e696f3798c1db3f0124c76b8f3531059369c0826dde7b42745ba7c7dbf925c9edfddc543ad68f1e2301bc6146955b784
7
- data.tar.gz: 70cb74c006fc864713f93f67615339fef2cc90f8e4776b355797c79ea80bd0fd66feb02d4357b1f643025fa31cfde99df468337590cf900d287a383fcc62b069
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.5: Bug fix
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
- draw_all
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
- update_header; update_titles; update_footer; update_left; update_main
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
- # Redraw the left‐pane list of weather + visibility blocks,
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
- case getchr
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
- return if @index == old_index
928
-
929
- update_left
930
- update_main
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
- # Fetch weather from met.no, but never let network errors crash the app
934
- def get_weather
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
- 12.times do |i|
1019
- date = (Date.today + i).strftime('%F')
1020
- ep = Ephemeris.new(date,@lat,@lon,@tz.to_i)
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, # e.g. 67.4
1024
- mph_s: ep.mph_s # e.g. "Waxing gibbous"
1121
+ mphase: ep.mphase,
1122
+ mph_s: ep.mph_s
1025
1123
  }
1026
- Ephemeris::BODY_ORDER.each do |b|
1027
- arr = ep.send(b)
1028
- entry[:"#{b}_rise"] = arr[5]
1029
- entry[:"#{b}_set"] = arr[7]
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.5'
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-23 00:00:00.000000000 Z
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.5:
32
- Bug fix.'
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