reflex-terminal 0.1.0

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/.doc/ext/reflex-terminal/native.cpp +19 -0
  3. data/.doc/ext/reflex-terminal/renderer.cpp +110 -0
  4. data/.doc/ext/reflex-terminal/terminal.cpp +530 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +12 -0
  6. data/.github/workflows/release-gem.yml +60 -0
  7. data/.github/workflows/tag.yml +35 -0
  8. data/.github/workflows/test.yml +46 -0
  9. data/.github/workflows/utils.rb +127 -0
  10. data/CONTRIBUTING.md +7 -0
  11. data/ChangeLog.md +6 -0
  12. data/Gemfile +5 -0
  13. data/LICENSE +21 -0
  14. data/README.md +100 -0
  15. data/Rakefile +44 -0
  16. data/VERSION +1 -0
  17. data/examples/terminal.rb +101 -0
  18. data/ext/reflex-terminal/defs.h +17 -0
  19. data/ext/reflex-terminal/extconf.rb +29 -0
  20. data/ext/reflex-terminal/native.cpp +19 -0
  21. data/ext/reflex-terminal/renderer.cpp +118 -0
  22. data/ext/reflex-terminal/terminal.cpp +572 -0
  23. data/include/reflex/terminal.h +233 -0
  24. data/include/reflex-terminal/defs.h +36 -0
  25. data/include/reflex-terminal/renderer.h +57 -0
  26. data/include/reflex-terminal/ruby/renderer.h +41 -0
  27. data/include/reflex-terminal/ruby/terminal.h +43 -0
  28. data/include/reflex-terminal/ruby.h +11 -0
  29. data/include/reflex-terminal.h +13 -0
  30. data/lib/reflex/bell_event.rb +15 -0
  31. data/lib/reflex/terminal.rb +168 -0
  32. data/lib/reflex/terminal_view.rb +268 -0
  33. data/lib/reflex-terminal/ext.rb +2 -0
  34. data/lib/reflex-terminal/extension.rb +41 -0
  35. data/lib/reflex-terminal.rb +7 -0
  36. data/reflex-terminal.gemspec +39 -0
  37. data/src/pty.cpp +236 -0
  38. data/src/renderer.cpp +365 -0
  39. data/src/terminal.cpp +1547 -0
  40. data/src/terminal.h +66 -0
  41. data/src/win32/pty.cpp +387 -0
  42. data/test/helper.rb +11 -0
  43. data/test/test_renderer.rb +113 -0
  44. data/test/test_terminal.rb +683 -0
  45. data/test/test_terminal_view.rb +182 -0
  46. metadata +152 -0
@@ -0,0 +1,60 @@
1
+ name: Release Gem
2
+
3
+ on:
4
+ push:
5
+ tags: ['v[0-9]*']
6
+
7
+ permissions:
8
+ contents: write
9
+
10
+ jobs:
11
+ release:
12
+ runs-on: macos-latest
13
+
14
+ steps:
15
+ - name: ruby 3.2
16
+ uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: 3.2
19
+
20
+ - name: checkout
21
+ uses: actions/checkout@v4
22
+
23
+ - name: setup gems
24
+ run: bundle install
25
+
26
+ - name: setup dependencies
27
+ run: "ruby -I.github/workflows -rutils -e 'setup_dependencies'"
28
+
29
+ - name: packages
30
+ run: bundle exec rake packages
31
+
32
+ - name: setup zig
33
+ if: github.event.repository.name == 'reflex-terminal'
34
+ uses: mlugg/setup-zig@v2
35
+ with:
36
+ version: 0.16.0
37
+
38
+ - name: test
39
+ run: bundle exec rake test
40
+
41
+ - name: create gem
42
+ id: gem
43
+ run: |
44
+ bundle exec rake gem
45
+ echo path=$(ruby -e 'print Dir.glob("*.gem").first') >> $GITHUB_OUTPUT
46
+
47
+ - name: create github release
48
+ env:
49
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50
+ run: ruby -I.github/workflows -rutils -e 'release(*ARGV)' ./${{ steps.gem.outputs.path }}
51
+
52
+ - name: upload to rubygems
53
+ env:
54
+ GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_AUTH_TOKEN }}
55
+ run: |
56
+ mkdir -p $HOME/.gem
57
+ touch $HOME/.gem/credentials
58
+ chmod 0600 $HOME/.gem/credentials
59
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
60
+ bundle exec rake upload
@@ -0,0 +1,35 @@
1
+ name: Tag
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+
7
+ jobs:
8
+ tag:
9
+ runs-on: ubuntu-latest
10
+
11
+ steps:
12
+ - name: ruby 3.2
13
+ uses: ruby/setup-ruby@v1
14
+ with:
15
+ ruby-version: 3.2
16
+
17
+ - name: checkout
18
+ uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 0
21
+ token: ${{ secrets.PAT }}
22
+
23
+ - name: setup dependencies
24
+ run: "ruby -I.github/workflows -rutils -e 'setup_dependencies only: :xot'"
25
+
26
+ - name: setup user name and email
27
+ run: |
28
+ git config --global user.email "xordog@gmail.com"
29
+ git config --global user.name "xord"
30
+
31
+ - name: tag versions
32
+ run: "ruby -I.github/workflows -rutils -e 'tag_versions'"
33
+
34
+ - name: push tags
35
+ run: git push origin --tags
@@ -0,0 +1,46 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: macos-latest
11
+
12
+ steps:
13
+ - name: ruby 3.2
14
+ uses: ruby/setup-ruby@v1
15
+ with:
16
+ ruby-version: 3.2
17
+
18
+ - name: checkout
19
+ uses: actions/checkout@v4
20
+
21
+ - name: setup gems
22
+ run: bundle install
23
+
24
+ - name: setup dependencies
25
+ run: "ruby -I.github/workflows -rutils -e 'setup_dependencies'"
26
+
27
+ - name: packages
28
+ run: bundle exec rake verbose packages
29
+
30
+ - name: setup zig
31
+ if: github.event.repository.name == 'reflex-terminal'
32
+ uses: mlugg/setup-zig@v2
33
+ with:
34
+ version: 0.16.0
35
+
36
+ - name: vendor
37
+ run: bundle exec rake vendor
38
+
39
+ - name: lib
40
+ run: bundle exec rake verbose lib
41
+
42
+ - name: ext
43
+ run: bundle exec rake verbose ext
44
+
45
+ - name: test
46
+ run: bundle exec rake verbose test
@@ -0,0 +1,127 @@
1
+ require 'shellwords'
2
+
3
+ ALL_REPO = 'xord/all'
4
+ ALL_DIR = '../all'
5
+ ALL_FETCH_DEPTH = 100
6
+
7
+ RENAMES = {reflex: 'reflexion'}
8
+
9
+ def sh(cmd)
10
+ puts cmd
11
+ system cmd
12
+ end
13
+
14
+ def setup_dependencies(only: nil)
15
+ gemspec_path = `git ls-files`.lines(chomp: true).find {|l| l =~ /\.gemspec$/}
16
+ return unless gemspec_path
17
+
18
+ gemspec = File.read gemspec_path
19
+ name = File.basename gemspec_path, '.gemspec'
20
+
21
+ exts = File.readlines('Rakefile')
22
+ .map {|l| l[%r|^\s*require\W+([\w\-\_]+)/extension\W+$|, 1]}
23
+ .compact
24
+ .reject {|ext| ext == name}
25
+ exts = exts & [only].flatten.map(&:to_s) if only
26
+ return if exts.empty?
27
+
28
+ unless setup_dependencies_via_monorepo(exts)
29
+ setup_dependencies_via_each_repo_by_version(gemspec, exts)
30
+ end
31
+
32
+ exts.each {|ext| sh %( cd ../#{ext} && rake ext )}
33
+ end
34
+
35
+ def setup_dependencies_via_monorepo(exts)
36
+ return false unless checkout_monorepo
37
+ exts.each {|ext| sh %( ln -snf all/#{ext} ../#{ext} )}
38
+ true
39
+ end
40
+
41
+ def checkout_monorepo()
42
+ uuid = `git log -1 --format=%B`[/^\[\[([0-9a-fA-F-]+)\]\]$/, 1]
43
+ return false unless uuid
44
+
45
+ commit = setup_monorepo uuid
46
+ return false unless commit
47
+
48
+ Dir.chdir(ALL_DIR) {sh %( git checkout -q #{commit} )}
49
+ true
50
+ end
51
+
52
+ def setup_monorepo(uuid)
53
+ unless File.directory? ALL_DIR
54
+ url = "https://github.com/#{ALL_REPO}.git"
55
+ sh %( git clone --no-tags --depth #{ALL_FETCH_DEPTH} #{url} #{ALL_DIR} )
56
+ end
57
+ loop do
58
+ commit = find_monorepo_commit uuid
59
+ return commit if commit
60
+
61
+ deepened = Dir.chdir ALL_DIR do
62
+ before = `git rev-list --count HEAD`.to_i
63
+ sh %( git fetch --deepen #{ALL_FETCH_DEPTH} )
64
+ `git rev-list --count HEAD`.to_i > before
65
+ end
66
+ return nil unless deepened
67
+ end
68
+ end
69
+
70
+ def find_monorepo_commit(uuid)
71
+ Dir.chdir ALL_DIR do
72
+ out = `git log origin/HEAD -F --grep="[[#{uuid}]]" --format=%H -1`.strip
73
+ out.empty? ? nil : out
74
+ end
75
+ end
76
+
77
+ def setup_dependencies_via_each_repo_by_version(gemspec, exts)
78
+ exts.each do |ext|
79
+ gem = RENAMES[ext.to_sym].then {|s| s || ext}
80
+ ver = gemspec[/add_dependency.*['"]#{gem}['"].*['"]\s*~>\s*([\d\.]+)\s*['"]/, 1]
81
+ opts = '-c advice.detachedHead=false --depth 1'
82
+ clone = "git clone #{opts} https://github.com/xord/#{ext}.git ../#{ext}"
83
+
84
+ # 'rake subtree:push' pushes all subrepos, so cloning by new tag
85
+ # often fails before tagging each new tag
86
+ sh %( #{clone} --branch v#{ver} || #{clone} )
87
+ end
88
+ end
89
+
90
+ def tag_versions()
91
+ changes = changelogs
92
+ tags = `git tag`.lines chomp: true
93
+ vers = `git log --oneline ./VERSION`
94
+ .lines(chomp: true)
95
+ .map {|line| line.split.first[/^\w+$/]}
96
+ .map {|sha| [`git cat-file -p #{sha}:./VERSION 2>/dev/null`[/[\d\.]+/], sha]}
97
+ .select {|ver, sha| ver && sha}
98
+ .reverse
99
+ .to_h
100
+
101
+ vers.to_a.reverse.each do |ver, sha|
102
+ tag = "v#{ver}"
103
+ break if tags.include?(tag)
104
+ sh %( git tag -a -m \"#{changes[tag]&.gsub '"', '\\"'}\" #{tag} #{sha} )
105
+ end
106
+ end
107
+
108
+ def release(*paths)
109
+ tag = ENV['GITHUB_REF']&.sub(%r|^refs/tags/|, '') || raise('GITHUB_REF tag not set')
110
+ notes = (changelogs[tag] || '').shellescape
111
+ paths = paths.flatten.join ' '
112
+
113
+ sh(%( gh release create #{tag} #{paths} --notes #{notes} )) ||
114
+ sh(%( gh release upload #{tag} #{paths} --clobber )) ||
115
+ raise('failed to upload to releases')
116
+ end
117
+
118
+ def changelogs()
119
+ File.read('ChangeLog.md')
120
+ .split(/^\s*##\s*\[\s*(v[\d\.]+)\s*\].*$/)
121
+ .slice(1..)
122
+ .each_slice(2)
123
+ .to_h
124
+ .transform_values(&:strip!)
125
+ rescue Errno::ENOENT
126
+ raise 'failed to get changelogs'
127
+ end
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,7 @@
1
+ # Contribution Guide
2
+
3
+ Thank you for your interest in contributing!
4
+ However, this repository does not accept pull requests.
5
+ Instead, please submit your changes to the [xord/all](https://github.com/xord/all) monorepo, which serves as the primary repository for all our main libraries.
6
+
7
+ For any questions, feel free to open an issue.
data/ChangeLog.md ADDED
@@ -0,0 +1,6 @@
1
+ # reflex-terminal ChangeLog
2
+
3
+
4
+ ## [v0.1.0] - 2026-08-04
5
+
6
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake'
4
+ gem 'test-unit'
5
+ gem 'yard'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xord.org
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # Reflex Terminal - A terminal emulator for Reflex
2
+
3
+ **Status: Usable, but the API is not stable yet.**
4
+
5
+ reflex-terminal provides a terminal emulator for the
6
+ [Reflex](https://github.com/xord/reflex) GUI toolkit:
7
+
8
+ - `Reflex::Terminal` — a headless terminal emulator model. It spawns a shell
9
+ on a PTY and maintains the screen state (cells, colors, scrollback, reflow
10
+ on resize) using [libghostty-vt](https://libghostty.tip.ghostty.org/),
11
+ the terminal emulation core extracted from
12
+ [Ghostty](https://github.com/ghostty-org/ghostty).
13
+ - `Reflex::TerminalView` — a `Reflex::View` that renders a `Terminal` and
14
+ feeds keyboard / mouse input into it.
15
+
16
+ Full-screen programs work: emacs and mouse-driven terminal multiplexers run
17
+ in it, with colors, CJK text, reflow on resize and scrollback.
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ require 'reflex-terminal'
23
+
24
+ win = Reflex::Window.new {
25
+ title 'Terminal'
26
+ frame 100, 100, 720, 450
27
+ }
28
+ win.add Reflex::TerminalView.new
29
+ win.show
30
+
31
+ Reflex.start
32
+ ```
33
+
34
+ See `examples/terminal.rb`.
35
+
36
+ ### Without a child process
37
+
38
+ A `Terminal` does not have to run a shell. Feed it bytes and it renders
39
+ whatever speaks terminal escape sequences — a build log, a recorded
40
+ session — with its colors and cursor motion applied:
41
+
42
+ ```ruby
43
+ t = Reflex::Terminal.new 120, 40
44
+ t.feed File.binread('build.log')
45
+ t.update
46
+ puts t.lines
47
+ ```
48
+
49
+ Driving an interactive program over your own transport takes one more call:
50
+ `#read_pending_input` hands back the bytes the terminal wants to send
51
+ upstream, which are the query responses it generates on its own plus any
52
+ encoded key and mouse events.
53
+
54
+ ## Requirements
55
+
56
+ - macOS, Linux or Windows
57
+ - Ruby 3.0+
58
+ - [Zig](https://ziglang.org/) **0.16.0** — required to build libghostty-vt
59
+ from source into `vendor/ghostty/`. On macOS `rake packages` installs it
60
+ through Homebrew; elsewhere take the 0.16.0 build for your platform from
61
+ the [downloads page](https://ziglang.org/download/) and put it on `PATH`,
62
+ since packaged versions rarely match the one the pinned ghostty commit
63
+ asks for.
64
+
65
+ ## Build
66
+
67
+ ```bash
68
+ $ rake packages # macOS: installs zig through Homebrew
69
+ $ rake ext # clones + builds libghostty-vt (first time only), then the extension
70
+ $ rake test
71
+ ```
72
+
73
+ ## Limitations
74
+
75
+ - No input method support, so Japanese and other composed text cannot be
76
+ typed. Reflex hands key events straight to the view without going through
77
+ the platform's input context, which is where composition would start.
78
+ - On Linux, `TerminalView` needs a font handed to it (`font:` with a
79
+ `Rays::Font` or a path). Rays cannot look one up by name there, so the
80
+ default font name does not resolve.
81
+ - On Windows, the single-string form of `Terminal#spawn` cannot contain
82
+ quotes. It runs through `cmd.exe /c`, which does not understand the
83
+ backslash escaping that the command line is built with. Pass the command
84
+ as separate arguments instead.
85
+ - No image protocols (Kitty graphics, Sixel).
86
+ - No selection or copy UI. `#lines` and `#each_history_line` return the text.
87
+
88
+ ## Updating the vendored libghostty-vt
89
+
90
+ libghostty-vt is under active development and has no standalone releases yet
91
+ (ghostty release tags up to v1.3.1 do not contain the terminal C APIs), so
92
+ this gem pins a verified commit of ghostty's main branch: see the `commit:`
93
+ given to `use_external_library` in `Rakefile`. To update:
94
+
95
+ 1. Bump that commit (check `minimum_zig_version` in ghostty's
96
+ `build.zig.zon` still matches the installed Zig)
97
+ 2. `rake vendor:ghostty:update`
98
+ 3. `rake ext test`
99
+
100
+ Once libghostty-vt gets standalone releases, switch the pin to a release tag.
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ # -*- mode: ruby -*-
2
+
3
+
4
+ %w[../xot ../rucy ../rays ../reflex .]
5
+ .map {|s| File.expand_path "#{s}/lib", __dir__}
6
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
7
+
8
+ require 'rucy/rake'
9
+
10
+ require 'xot/extension'
11
+ require 'rucy/extension'
12
+ require 'rays/extension'
13
+ require 'reflex/extension'
14
+ require 'reflex-terminal/extension'
15
+
16
+
17
+ EXTENSIONS = [Xot, Rucy, Rays, Reflex, ReflexTerminal]
18
+ TESTS_ALONE = []
19
+
20
+ install_packages osx: %w[zig]
21
+
22
+ use_external_library 'https://github.com/ghostty-org/ghostty',
23
+ # libghostty-vt is not released as a standalone library yet, so pin a
24
+ # verified commit on ghostty's main branch. (Release tags up to v1.3.1 do
25
+ # not contain the terminal/render C APIs.) Bump intentionally, then run
26
+ # 'rake vendor:ghostty:update' and re-run the tests.
27
+ commit: '2de5e7d38e1354759211722a8687c0815d2cf02c',# requires Zig 0.16.0
28
+ incdirs: 'zig-out/include',
29
+ srcdirs: 'NOSRC',
30
+ defs: 'GHOSTTY_STATIC',
31
+ &proc {
32
+ opts = '-Demit-lib-vt -Doptimize=ReleaseFast'
33
+ opts += ' -Dtarget=x86_64-windows-gnu' if win32?
34
+ sh %( zig build #{opts} )
35
+ sh %( mv zig-out/lib/libghostty-vt.a zig-out/lib/libghostty-vt-static.a ) unless win32?
36
+ }
37
+
38
+ default_tasks :ext
39
+ use_bundler
40
+ build_native_library
41
+ build_ruby_extension
42
+ test_ruby_extension unless github_actions? && win32?
43
+ generate_documents
44
+ build_ruby_gem
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,101 @@
1
+ %w[xot rucy rays reflex reflex-terminal]
2
+ .map {|s| File.expand_path "../../#{s}/lib", __dir__}
3
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
4
+
5
+ require 'reflex-terminal'
6
+
7
+
8
+ def copy(terminal)
9
+ text = terminal.selected_text
10
+ Reflex::Clipboard.text = text unless text.empty?
11
+ end
12
+
13
+ def copyable?(terminal)
14
+ terminal.selection?
15
+ end
16
+
17
+ def paste(terminal)
18
+ text = Reflex::Clipboard.text
19
+ return if text.to_s.empty?
20
+ terminal.scroll_to 0 if terminal.scroll != 0
21
+ terminal.paste text
22
+ end
23
+
24
+ def pasteable?()
25
+ !Reflex::Clipboard.text.to_s.empty?
26
+ end
27
+
28
+
29
+ terminal = Reflex::Terminal.new.spawn
30
+ view = Reflex::TerminalView.new terminal: terminal, font_size: 24
31
+ shortcut = Xot.osx? ? %i[command] : %i[control shift]
32
+ taken = []
33
+ flash = 0
34
+ flash_timer = nil
35
+
36
+ view.after :on_draw do |e|
37
+ next if flash <= 0
38
+ e.painter.fill 1, flash
39
+ e.painter.rect e.bounds
40
+ flash *= 0.9
41
+ flash = 0 if flash < 0.01
42
+ end
43
+
44
+ view.before :on_key_down do |e|
45
+ # locks are states the keyboard is left in rather than keys held for
46
+ # the stroke, so a shortcut has to match with them left out
47
+ next unless e.modifiers(locks: false).sort == shortcut.sort
48
+
49
+ case e.code
50
+ when Reflex::KEY_C then copy terminal
51
+ when Reflex::KEY_V then paste terminal
52
+ else next
53
+ end
54
+
55
+ # the press is taken, so its release has to be taken with it or the
56
+ # child is told a key went up that it never saw go down. The modifier
57
+ # can be let go first, so the key itself is what has to be remembered
58
+ taken |= [e.code]
59
+ :skip
60
+ end
61
+
62
+ view.before :on_key_up do |e|
63
+ :skip if taken.delete e.code
64
+ end
65
+
66
+ # on the release, not the press: a native menu runs an event loop of its
67
+ # own and swallows the mouse up, which would leave the window believing the
68
+ # button was still down and every later drag starting where it was clicked
69
+ view.before :on_pointer_up do |e|
70
+ next unless e.right?
71
+ m = Reflex::Menu.new
72
+
73
+ c = m.add Reflex::Menu.new('Copy')
74
+ c.on(:show) {|e| c.enable copyable?(terminal)}
75
+ c.on(:click) {|e| copy terminal}
76
+
77
+ p = m.add Reflex::Menu.new('Paste')
78
+ p.on(:show) {|e| p.enable pasteable?}
79
+ p.on(:click) {|e| paste terminal}
80
+
81
+ m.popup view, e.x, e.y
82
+ end
83
+
84
+ view.on :bell do |e|
85
+ flash = 0.8
86
+
87
+ flash_timer&.stop
88
+ flash_timer = view.interval do
89
+ view.redraw
90
+ flash_timer.stop if flash <= 0
91
+ end
92
+ end
93
+
94
+ win = Reflex::Window.new do
95
+ title 'Terminal Example'
96
+ frame 100, 100, 720, 450
97
+ end
98
+ win.add view
99
+ win.show
100
+
101
+ Reflex.start
@@ -0,0 +1,17 @@
1
+ // -*- c++ -*-
2
+ #pragma once
3
+ #ifndef __REFLEX_TERMINAL_EXT_DEFS_H__
4
+ #define __REFLEX_TERMINAL_EXT_DEFS_H__
5
+
6
+
7
+ #include <rucy.h>
8
+ #include "reflex/defs.h"
9
+ #include "reflex/ruby/defs.h"
10
+
11
+
12
+ using namespace Xot::Types;
13
+
14
+ using namespace Rucy;
15
+
16
+
17
+ #endif//EOH
@@ -0,0 +1,29 @@
1
+ %w[../xot ../rucy ../rays ../reflex .]
2
+ .map {|s| File.expand_path "../../#{s}/lib", __dir__}
3
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
4
+
5
+ require 'mkmf'
6
+ require 'xot/extconf'
7
+ require 'xot/extension'
8
+ require 'rucy/extension'
9
+ require 'rays/extension'
10
+ require 'reflex/extension'
11
+ require 'reflex-terminal/extension'
12
+
13
+
14
+ Xot::ExtConf.new Xot, Rucy, Rays, Reflex, ReflexTerminal do
15
+ setup do
16
+ vendor = File.expand_path '../../vendor/ghostty', __dir__
17
+
18
+ headers << 'ruby.h'
19
+ defs << 'GHOSTTY_STATIC'
20
+ inc_dirs << "#{vendor}/zig-out/include"
21
+ lib_dirs << "#{vendor}/zig-out/lib"
22
+ local_libs << 'util' if linux?
23
+ local_libs << 'ntdll' if win32?
24
+ local_libs << 'ghostty-vt-static'
25
+ $LDFLAGS << ' -Wl,--out-implib=libreflex-terminal.dll.a' if mingw? || cygwin?
26
+ end
27
+
28
+ create_makefile 'reflex_terminal_ext'
29
+ end
@@ -0,0 +1,19 @@
1
+ #include "defs.h"
2
+
3
+
4
+ void Init_reflex_terminal ();
5
+ void Init_reflex_terminal_renderer ();
6
+
7
+
8
+ extern "C" void
9
+ Init_reflex_terminal_ext ()
10
+ {
11
+ RUCY_TRY
12
+
13
+ Rucy::init();
14
+
15
+ Init_reflex_terminal();
16
+ Init_reflex_terminal_renderer();
17
+
18
+ RUCY_CATCH
19
+ }