summaryrefslogtreecommitdiffhomepage
path: root/dragon/hotload.rb
blob: 1196e2b86b36399530bdec58d79a0599db92978b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# hotlaod.rb has been released under MIT (*only this file*).

module GTK
  class Runtime
    # @visibility private
    module Hotload
      def hotload_init
        @hotload_if_needed = 0
        @mailbox_if_needed = 0

        # schema for file_mtimes
        # { FILE_PATH: { current: (Time as Fixnum),
        #                last:    (Time as Fixnum) },
        #   FILE_PATH: { current: (Time as Fixnum),
        #                last:    (Time as Fixnum) } }
        @file_mtimes = { }

        @suppress_mailbox = true
        files_to_reload.each { |f| init_mtimes f }
        init_mtimes 'app/mailbox.rb'
      end

      def hotload_on_write_file file_name
        return unless file_name.include? 'mailbox.rb'
        @mailbox_if_needed = :force
      end

      def files_to_reload
        if @rcb_release_mode
          core_files_to_reload + @required_files
        else
          [
            'dragon/docs.rb',
            'dragon/help.rb',
            'dragon/kernel_docs.rb',
            'dragon/kernel.rb',
            'dragon/easing.rb',
            'dragon/top_level.rb',
            'dragon/log.rb',
            'dragon/geometry.rb',
            'dragon/attr_gtk.rb',
            'dragon/attr_sprite.rb',
            'dragon/object.rb',
            'dragon/class.rb',
            'dragon/string.rb',
            'dragon/entity.rb',
            'dragon/strict_entity.rb',
            'dragon/open_entity.rb',
            'dragon/serialize.rb',
            'dragon/primitive.rb',
            'dragon/nil_class_false_class.rb',
            'dragon/symbol.rb',
            'dragon/numeric_deprecated.rb',
            'dragon/numeric.rb',
            'dragon/hash_deprecated.rb',
            'dragon/hash.rb',
            'dragon/outputs_deprecated.rb',
            'dragon/array_docs.rb',
            'dragon/array.rb',
            'dragon/outputs.rb',
            'dragon/inputs.rb',
            'dragon/mouse_docs.rb',
            'dragon/recording.rb',
            'dragon/grid.rb',
            'dragon/layout.rb',
            'dragon/args_deprecated.rb',
            'dragon/fn.rb',
            'dragon/args.rb',
            'dragon/console_prompt.rb',
            'dragon/console_menu.rb',
            'dragon/console.rb',
            'dragon/assert.rb',
            'dragon/tests.rb',
            'dragon/controller_config.rb',
            'dragon/runtime/draw.rb',
            'dragon/runtime/deprecated.rb',
            'dragon/runtime/framerate.rb',
            'dragon/runtime/c_bridge.rb',
            'dragon/runtime/hotload.rb',
            'dragon/runtime/backup.rb',
            'dragon/runtime/async_require.rb',
            'dragon/runtime/autocomplete.rb',
            'dragon/api.rb',
            'dragon/runtime.rb',
            'dragon/trace.rb',
            'dragon/readme_docs.rb',
            'dragon/hotload_client.rb',
            'dragon/wizards.rb',
            'dragon/ios_wizard.rb',
            'dragon/itch_wizard.rb',
          ] + core_files_to_reload + @required_files
        end
      end

      def core_files_to_reload
        [
          'repl.rb',
          'app/main.rb',
          'app/repl.rb',
          'app/tests.rb',
          'app/test.rb',
          'app/stdin.rb'
        ]
      end

      def init_mtimes file
        @file_mtimes[file] ||= { current: @ffi_file.mtime(file),
                                 last: @ffi_file.mtime(file) }
      end

      def hotload_source_files
        @hotload_if_needed += 1
        return unless @hotload_if_needed == 60
        @hotload_if_needed = 0
        files_to_reload.each { |f| reload_if_needed f }
        console.enable
      end

      def mailbox_timeout
        if @suppress_mailbox
          60
        else
          3
        end
      end

      def check_mailbox
        if @mailbox_if_needed == :force # lol
          reload_if_needed 'app/mailbox.rb', true
          @mailbox_if_needed = 1
          return
        end
        @mailbox_if_needed += 1
        return unless @mailbox_if_needed.mod_zero? mailbox_timeout
        @mailbox_if_needed = 1
        reload_if_needed 'app/mailbox.rb'
      end

      def hotload_if_needed
        return if Kernel.tick_count < 0
        hotload_source_files
        check_mailbox
      end

      def on_load_succeeded file
        self.files_reloaded << file
        self.reloaded_files << file
        Trace.untrace_classes!
      end

      def reset_all_mtimes
        @file_mtimes.each do |file, _|
          @file_mtimes[file].current = @ffi_file.mtime(file)
          @file_mtimes[file].last    = @file_mtimes[file].current
        end

        files_to_reload.each do |file, _|
          @file_mtimes[file] ||= {}
          @file_mtimes[file].current = @ffi_file.mtime(file)
          @file_mtimes[file].last    = @file_mtimes[file].current
        end
      end

      def reload_if_needed file, force = false
        @file_mtimes[file] ||= { current: @ffi_file.mtime(file), last: @ffi_file.mtime(file) }
        @file_mtimes[file].current = @ffi_file.mtime(file)
        return if !force && @file_mtimes[file].current == @file_mtimes[file].last
        on_load_succeeded file if reload_ruby_file file
        @file_mtimes[file].last = @file_mtimes[file].current
      end
    end
  end
end