├── .gitignore
├── .gitmodules
├── COPYING
├── LICENSE
├── README.md
├── Rakefile
├── TODO.md
├── darwin
├── SDLMain.h
└── SDLMain.m
├── docs
└── screenshot.png
├── font
├── DejaVuSansMono-Bold.ttf
├── DejaVuSansMono.ttf
├── SourceCodePro-Black.ttf
├── SourceCodePro-Bold.ttf
└── SourceCodePro-Regular.ttf
├── gen_shaders.rb
├── joystick.cpp
├── joystick.h
├── keyboard.cpp
├── keyboard.h
├── kill.rb
├── mintty
├── README.md
├── charset.c
├── charset.h
├── child.c
├── child.h
├── config.c
├── config.h
├── minibidi.c
├── minibidi.h
├── std.c
├── std.h
├── term.c
├── term.h
├── termclip.c
├── termline.c
├── termout.c
├── termpriv.h
├── win.cpp
└── win.h
├── opengl.h
├── package
└── mac_app
│ └── Contents
│ ├── Info.plist
│ ├── MacOS
│ └── launch
│ └── Resources
│ └── icon.icns
├── pty.c
├── pty.h
├── render.cpp
├── render.h
├── restart.rb
├── riftty.cpp
├── shader.cpp
├── shader.h
├── shader
├── fullbright.fsh
├── fullbright.vsh
├── fullbright_textured.fsh
├── fullbright_textured.vsh
├── fullbright_textured_text.fsh
├── fullbright_textured_vert_color.fsh
├── fullbright_textured_vert_color.vsh
├── fullbright_textured_vert_color_text.fsh
├── fullbright_vert_color.fsh
├── fullbright_vert_color.vsh
├── phong_textured.fsh
└── phong_textured.vsh
└── vnc-helper
/.gitignore:
--------------------------------------------------------------------------------
1 | riftty
2 | *.o
3 | *.d
4 | TAGS
5 | .DS_Store
6 |
7 | # generated app files
8 | package/riftty.app/
9 | package/riftty.zip
10 |
11 | # don't check in "secret" deploy script
12 | deploy
13 |
14 | # generated shader headers
15 | FullbrightShader.h
16 | FullbrightVertColorShader.h
17 | FullbrightTexturedShader.h
18 | FullbrightTexturedTextShader.h
19 | FullbrightTexturedVertColorShader.h
20 | FullbrightTexturedVertColorTextShader.h
21 | PhongTexturedShader.h
22 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "glyphblaster"]
2 | path = glyphblaster
3 | url = https://github.com/hyperlogic/glyphblaster.git
4 | [submodule "abaci"]
5 | path = abaci
6 | url = https://github.com/hyperlogic/abaci.git
7 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | riftty is a virtual reality terminal emulation program.
2 | Copyright (C) 2012 Anthony J. Thibault
3 |
4 | This program is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This program is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | riftty
2 | ============
3 |
4 | This repo is now read-only and archived, it uses an old Oculus SDK from DK1 era and is unlikely to work on newer HMDs.
5 |
6 | Terminal emulator meant for use with the Oculus Rift headseat.
7 |
8 | Built with Oculus SDK 0.4.1 for DK2 support.
9 |
10 | 
11 |
12 | ## Download
13 |
14 | [riftty.zip](http://www.hyperlogic.org/riftty.zip) - For MacOSX 10.9
15 |
16 | ## Configuration
17 |
18 | To customize the configuraiton create a `.riffty` file in your home directory.
19 | Each line in the file should be in the form Key=Value.
20 |
21 | Here is an example config:
22 |
23 | SwapAltAndMetaKeys=true
24 | Columns=80
25 | Rows=25
26 | WinPosX=0
27 | WinPosY=1.7
28 | WinPosZ=-1.0
29 | WinFullscreen=false
30 | WinWidth=2.0
31 | Font=font/SourceCodePro-Regular.ttf
32 | FontSize=64
33 |
34 | ### Font
35 |
36 | * **Font** (string) file location of a FreeType2 compatible font.
37 | * **FontSize** (int) This won't change the size of the letters, but only how crisp they are when viewed up close.
38 | Don't use larger then 64 or so. 32 is the default.
39 |
40 | ### Terminal settings
41 |
42 | * **Columns** (int) number of columns in the terminal. 80 columns is the default.
43 | * **Rows** (int) number of rows in the terminal. 24 columns is the default.
44 | * **Term** (string) reported in the TERM enviornment variable.
45 |
46 | ### Window positioning
47 |
48 | * **WinPosX** (float) Used to move terminal window in world space.
49 | Negative values will move the terminal to the left, positive to the right. 0 is the default.
50 | * **WinPosY** (float) Used to move the terminal window in world space.
51 | Positive values will move the terminal up, negative will move it down, Possibly below the floor.
52 | 1 is the default.
53 | * **WinPosZ** (float) Used to move the position of the terminal window in world space.
54 | The view is facing down the negative Z axis, so -1 will be 1 meter from your face, -10 will be 10 meters from your face.
55 | -1 is the default.
56 | Avoid positive values as they will be behind your head in the starting orientation.
57 | * **WinWidth** (float) The width of the terminal in the world. 2 meters is the default
58 | * **WinRotX** (float) Tilt the terminal around the x axis in degrees. 0 is the default.
59 | * **WinRotY** (float) Tilt the terminal around the y axis in degrees. 0 is the default.
60 | * **WinRotZ** (float) Tilt the terminal around the z axis in degrees. 0 is the default.
61 |
62 | ### Misc
63 |
64 | * **WinFullscreen** (bool) when false the application will run in a window instead of fullscreen. true is the default.
65 | * **SwapAltAndMetaKeys** (bool) when true the option and command keys are swapped. The default value is false.
66 |
67 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rake/clean'
2 |
3 | $PLATFORM = `uname`.chomp
4 |
5 | if $PLATFORM == 'Darwin'
6 | $CC = 'clang'
7 | elsif $PLATFORM == 'Linux'
8 | $CC = 'gcc'
9 | end
10 |
11 | # used by tags task
12 | class Dir
13 | # for each file in path, including sub-directories,
14 | # yield the full path of the filename to a block
15 | def Dir.for_each_rec path, &block
16 | if File.directory?(path)
17 | foreach path do |file|
18 | if file[0,1] != '.'
19 | for_each_rec File.expand_path(file, path), &block
20 | end
21 | end
22 | else
23 | yield path
24 | end
25 | end
26 | end
27 |
28 | # different flags for c and cpp
29 | $C_FLAGS = ['-Wall',
30 | `sdl2-config --cflags`.chomp,
31 | `freetype-config --cflags`.chomp,
32 | "-Iglyphblaster/src",
33 | "-Iabaci/src",
34 | "-Imintty/",
35 | ]
36 |
37 | $CPP_FLAGS = ['-Wall',
38 | '--std=c++11',
39 | `sdl2-config --cflags`.chomp,
40 | `freetype-config --cflags`.chomp,
41 | "-Iglyphblaster/src",
42 | "-Iabaci/src",
43 | "-Imintty/",
44 | "-fno-rtti"
45 | ]
46 |
47 | $DEBUG_C_FLAGS = ['-g','-DDEBUG']
48 | $OPT_C_FLAGS = ['-O3', '-DNDEBUG'];
49 |
50 | $L_FLAGS = [`sdl2-config --libs`.chomp,
51 | `freetype-config --libs`.chomp,
52 | '-lstdc++',
53 | # '-lharfbuzz',
54 | ]
55 |
56 | if $PLATFORM == 'Darwin'
57 | $C_FLAGS += ['-DDARWIN', "-I../OculusSDK/LibOVR/Include"]
58 | $CPP_FLAGS += ['-DDARWIN', "-I../OculusSDK/LibOVR/Include"]
59 | $L_FLAGS += ["-F../OculusSDK/LibOVR/Lib/Mac/Release/",
60 | '-framework Cocoa',
61 | '-framework OpenGL',
62 | '-framework CoreServices',
63 | '-framework ApplicationServices',
64 | '-framework IOKit',
65 | '-framework LibOVR']
66 | elsif $PLATFORM == 'Linux'
67 | $C_FLAGS += ['-DLINUX', "-I../ovr_sdk_linux_0.5.0.1/LibOVR/Include", "-I../ovr_sdk_linux_0.5.0.1/LibOVRKernel/Src"]
68 | $CPP_FLAGS += ['-DLINUX', "-I../ovr_sdk_linux_0.5.0.1/LibOVR/Include", "-I../ovr_sdk_linux_0.5.0.1/LibOVRKernel/Src"]
69 | $L_FLAGS += ['-lGL', '-lm', '-lc', '-ldl', '-lutil', '-lOVR']
70 | end
71 |
72 | $OBJECTS = ['riftty.o',
73 | 'pty.o',
74 | 'keyboard.o',
75 | 'joystick.o',
76 | 'render.o',
77 | 'shader.o',
78 |
79 | # mintty
80 | 'mintty/config.o',
81 | 'mintty/term.o',
82 | 'mintty/termline.o',
83 | 'mintty/termout.o',
84 | 'mintty/termclip.o',
85 | 'mintty/minibidi.o',
86 | 'mintty/charset.o',
87 | 'mintty/child.o',
88 | 'mintty/std.o',
89 | 'mintty/win.o',
90 |
91 | # glyphblaster
92 | 'glyphblaster/src/cache.o',
93 | 'glyphblaster/src/context.o',
94 | 'glyphblaster/src/font.o',
95 | 'glyphblaster/src/glyph.o',
96 | 'glyphblaster/src/text.o',
97 | 'glyphblaster/src/texture.o',
98 | ]
99 |
100 | $GEN_HEADERS = ['FullbrightShader.h',
101 | 'FullbrightVertColorShader.h',
102 | 'FullbrightTexturedShader.h',
103 | 'FullbrightTexturedTextShader.h',
104 | 'FullbrightTexturedVertColorShader.h',
105 | 'FullbrightTexturedVertColorTextShader.h',
106 | 'PhongTexturedShader.h']
107 |
108 | $DEPS = $OBJECTS.map {|f| f[0..-3] + '.d'}
109 | $EXE = 'riftty'
110 |
111 | def flags_for src
112 | if File.extname(src) == '.cpp'
113 | $CPP_FLAGS.join ' '
114 | else
115 | $C_FLAGS.join ' '
116 | end
117 | end
118 |
119 | # Use the compiler to build makefile rules for us.
120 | # This will list all of the pre-processor includes this source file depends on.
121 | def make_deps t
122 | sh "#{$CC} -MM -MF #{t.name} #{flags_for t.source} -c #{t.source}"
123 | end
124 |
125 | # Compile a single compilation unit into an object file
126 | def compile obj, src
127 | sh "#{$CC} #{flags_for src} -c #{src} -o #{obj}"
128 | end
129 |
130 | # Link all the object files to create the exe
131 | def do_link exe, objects
132 | sh "#{$CC} #{objects.join ' '} -o #{exe} #{$L_FLAGS.join ' '}"
133 | end
134 |
135 | # generate makefile rules from source code
136 | rule '.d' => '.cpp' do |t|
137 | make_deps t
138 | end
139 | rule '.d' => '.c' do |t|
140 | make_deps t
141 | end
142 | rule '.d' => '.m' do |t|
143 | make_deps t
144 | end
145 |
146 | # adds .o rules so that objects will be recompiled if any of the contributing source code has changed.
147 | task :add_deps => $DEPS do
148 |
149 | $GEN_HEADERS.each do |header|
150 | file header => "gen_shaders.rb" do |t|
151 | sh './gen_shaders.rb'
152 | end
153 | end
154 |
155 | $OBJECTS.each do |obj|
156 | dep = obj[0..-3] + '.d'
157 | raise "Could not find dep file for object #{obj}" unless dep
158 |
159 | # open up the .d file, which is a makefile rule (built by make_deps)
160 | deps = []
161 | File.open(dep, 'r') {|f| f.each {|line| deps |= line.split}}
162 | deps.reject! {|x| x == '\\'} # remove '\\' entries
163 |
164 | # Add a new file rule which will build the object file from the source file.
165 | # Note: this object file depends on all the pre-processor includes as well
166 | file obj => deps[1,deps.size] do |t|
167 | compile t.name, t.prerequisites[0]
168 | end
169 | end
170 | end
171 |
172 | file :build_objs => $OBJECTS do
173 | end
174 |
175 | file :gen_shaders do
176 | rm_rf $EXE
177 | sh "./gen_shaders.rb"
178 | end
179 |
180 | file $EXE => [:add_deps, :build_objs] do
181 | do_link $EXE, $OBJECTS
182 | end
183 |
184 | task :build => $EXE
185 |
186 | task :add_opt_flags do
187 | $C_FLAGS += $OPT_C_FLAGS
188 | $CPP_FLAGS += $OPT_C_FLAGS
189 | end
190 | task :add_debug_flags do
191 | $C_FLAGS += $DEBUG_C_FLAGS
192 | $CPP_FLAGS += $DEBUG_C_FLAGS
193 | end
194 |
195 | desc "Optimized Build"
196 | task :opt => [:add_opt_flags, :gen_shaders, $EXE]
197 |
198 | desc "Debug Build"
199 | task :debug => [:add_debug_flags, :gen_shaders, $EXE]
200 |
201 | desc "Optimized Build, By Default"
202 | task :default => [:opt]
203 |
204 | desc "ctags for emacs"
205 | task :tags do
206 | sh "rm TAGS"
207 |
208 | PATHS = [File.expand_path('.')]
209 | # ends in .h, .cpp or .c (case insensitive match)
210 | SRC_PATTERN = /\.[hH]\z|\.[cC][pP][pP]\z|\.[cC]\z/
211 | src_files = []
212 |
213 | # fill temp_file with all the source files in PATHS
214 | PATHS.each do |path|
215 | Dir.for_each_rec(path) do |f|
216 | if SRC_PATTERN =~ f
217 | src_files << f
218 | end
219 | end
220 | end
221 |
222 | src_files.each {|f| sh "etags -a #{f}"}
223 | end
224 |
225 | desc "make an .app"
226 | task :package do
227 | package_app_macos "riftty"
228 | end
229 |
230 | def package_app_macos app_name
231 | puts "Packaging #{app_name} for MacOSX"
232 |
233 | make_package_macos app_name
234 |
235 | =begin
236 | # copy application into app dir
237 | if File.exists? "#{app_name}.rb"
238 | # simple app with only a single .rb file
239 | sh "cp #{app_name}.rb package/#{app_name}.app/Contents/MacOS/"
240 | elsif File.exists? "app/#{app_name}/#{app_name}.rb"
241 | # copy entire app sub-dir into package
242 | sh "cp -R app/#{app_name}/ package/#{app_name}.app/Contents/MacOS/app/#{app_name}/"
243 | else
244 | raise "Cannot Find app #{app_name}"
245 | end
246 | =end
247 |
248 | # zip it up
249 | sh "cd package; zip -rq #{app_name}.zip #{app_name}.app"
250 |
251 | # remove the app dir
252 | #sh "rm -rf package/#{app_name}.app"
253 |
254 | puts ""
255 | puts "Finished : package/#{app_name}.zip"
256 | end
257 |
258 | def gsub_file filename, pattern, replace
259 | str = File.open(filename, "r") {|file| file.read}
260 | str.gsub! pattern, replace
261 | File.open(filename, "w") {|file| file.write str}
262 | end
263 |
264 | def make_package_macos app_name
265 |
266 | # delete the zip and app if it already exists
267 | sh "rm -rf package/#{app_name}.zip"
268 | sh "rm -rf package/#{app_name}.app"
269 |
270 | # create the app dir & copy the base resources into it.
271 | sh "mkdir package/#{app_name}.app"
272 | sh "cp -r package/mac_app/ package/#{app_name}.app/"
273 |
274 | # Copy riftty into app/Contents/MacOS
275 | dir_list = ['shader', 'font']
276 | dir_list.each do |dir|
277 | sh "cp -r #{dir}/ package/#{app_name}.app/Contents/MacOS/#{dir}"
278 | end
279 |
280 | # copy exe
281 | file_list = ['riftty']
282 | file_list.each do |file|
283 | sh "cp #{file} package/#{app_name}.app/Contents/MacOS/"
284 | end
285 |
286 | # make sure wrapper script is executable.
287 | sh "chmod +x package/#{app_name}.app/Contents/MacOS/launch"
288 |
289 | # get a list of all non-system dylibs referenced
290 | dylibs = list_dylibs app_name, 'riftty'
291 |
292 | # print out libs
293 | # puts ""
294 | # puts "libs"
295 | # dylibs.each do |lib|
296 | # puts " #{lib}"
297 | # end
298 | # puts ""
299 |
300 | # copy dylibs into app dir
301 | dylibs.each do |lib|
302 | sh "cp #{lib} package/#{app_name}.app/Contents/MacOS/"
303 | sh "chmod 777 package/#{app_name}.app/Contents/MacOS/#{File.basename(lib)}"
304 | end
305 |
306 | # use install_name_tool to fix up the install_names
307 | (dylibs + ['riftty']).each do |lib|
308 | otool(lib).each do |sub_lib|
309 | sh "install_name_tool -change #{sub_lib} @executable_path/#{File.basename sub_lib} package/#{app_name}.app/Contents/MacOS/#{File.basename lib}"
310 | end
311 | end
312 |
313 | end
314 |
315 | # returns the non-system dependencies of a given dylib
316 | def otool filename
317 | # run otool to get a list of dylib dependencies.
318 | otool_output = `otool -L #{filename}`
319 |
320 | # strip out everything except the dylib path
321 | dylibs = otool_output.split("\n").map {|line| line.lstrip}[1..-1]
322 | dylibs.map! {|line| line.split("(")[0].rstrip}
323 |
324 | # remove any dylibs in /System/Library or /usr/lib
325 | dylibs.delete_if {|lib| lib =~ /(System\/Library\/)|(usr\/lib\/)/}
326 | dylibs
327 | end
328 |
329 | # recursivly builds a list of a libs runtime dependencies
330 | def list_dylibs app_name, filename, list = []
331 | otool(filename).each do |lib|
332 | if !list.include? lib
333 | list.push lib
334 | list_dylibs app_name, lib, list
335 | end
336 | end
337 | list
338 | end
339 |
340 |
341 |
342 | CLEAN.include $DEPS, $OBJECTS, $GEN_HEADERS
343 | CLOBBER.include $EXE
344 |
345 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | # TODO
2 |
3 | ## Platforms
4 | * Linux (pending SDK udpate)
5 | * Windows... I suppose there would be value here?
6 |
7 | ## Configuration
8 | Add more configuration variables to .riftty config file (mintty/config.c)
9 | * transparency
10 | * color schemes
11 |
12 | ## Performance Improvements
13 | On DK2 with a 50+ rows text, my laptop can't keep 75htz.
14 |
15 | First pass, reduce draw calls, currently there is one call per-glyph, and two extra calls per string (bg)
16 |
17 | VBO's per line is probably the way to go, this way they don't have to be rebuilt
18 | when scrolling, the floor shader is per-pixel and dynamic, probably a static light map would
19 | be better.
20 |
21 | ## Keyboard Shortcuts
22 | Keybindings for riftty specific things such as:
23 | * view recenter
24 | * standing vs sitting
25 | * display / hide fps or other data.
26 | * move screen in/out
27 | * increase/decrease screen size
28 | * move screen left/right/up/down
29 | * increase decrease terminal geometry (rows, cols)
30 |
31 | The problem here is that we need some special way to escape keybindings so they don't go to the
32 | terminal, but instead are interpreted by the system. Perhaps hooking up mouse would help here...
33 |
34 | ## Mouse Support
35 | * scroll back (middle mouse wheel), might be useful, even without actual cursor support.
36 | * click to place cursor
37 | * select for cut and paste.
38 | * fade out when typing.
39 | * eventually would be useful for switching keyboard focus between terminals
40 | * configurable cursor
41 |
42 | ## Future Experiments
43 | * multiple panels! mintty would need some serious re-factoring for this, so many globals.
44 | unless we use seperate processes and ipc, also a serious refactor.
45 | * curved screen! cylindrical, spherical.
46 | * avatar!
47 | * sky skinning - colors, textures
48 | * floor skinning - tile size, textures
49 | * dynamic monitor glow on your avatar and the environment!
50 | * pluggable 3d scenes (would probably want to switch to a real render for this)
51 | * would like to experiment with history extending past the extent of the
52 | terminal bounds, extending words upwards and fading out.
53 | * tmux integration? iTerm2 does some cool stuff here..
54 |
55 |
--------------------------------------------------------------------------------
/darwin/SDLMain.h:
--------------------------------------------------------------------------------
1 | /* SDLMain.m - main entry point for our Cocoa-ized SDL app
2 | Initial Version: Darrell Walisser
3 | Non-NIB-Code & other changes: Max Horn
4 |
5 | Feel free to customize this file to suit your needs
6 | */
7 |
8 | #import
9 |
10 | @interface SDLMain : NSObject
11 | @end
12 |
--------------------------------------------------------------------------------
/darwin/SDLMain.m:
--------------------------------------------------------------------------------
1 | /* SDLMain.m - main entry point for our Cocoa-ized SDL app
2 | Initial Version: Darrell Walisser
3 | Non-NIB-Code & other changes: Max Horn
4 |
5 | Feel free to customize this file to suit your needs
6 | */
7 |
8 | #import "SDL.h"
9 | #import "SDLMain.h"
10 | #import /* for MAXPATHLEN */
11 | #import
12 |
13 | /* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
14 | but the method still is there and works. To avoid warnings, we declare
15 | it ourselves here. */
16 | @interface NSApplication(SDL_Missing_Methods)
17 | - (void)setAppleMenu:(NSMenu *)menu;
18 | @end
19 |
20 | /* Use this flag to determine whether we use SDLMain.nib or not */
21 | #define SDL_USE_NIB_FILE 0
22 |
23 | /* Use this flag to determine whether we use CPS (docking) or not */
24 | #define SDL_USE_CPS 1
25 | #ifdef SDL_USE_CPS
26 | /* Portions of CPS.h */
27 | typedef struct CPSProcessSerNum
28 | {
29 | UInt32 lo;
30 | UInt32 hi;
31 | } CPSProcessSerNum;
32 |
33 | extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
34 | extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
35 | extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
36 |
37 | #endif /* SDL_USE_CPS */
38 |
39 | static int gArgc;
40 | static char **gArgv;
41 | static BOOL gFinderLaunch;
42 | static BOOL gCalledAppMainline = FALSE;
43 |
44 | static NSString *getApplicationName(void)
45 | {
46 | NSDictionary *dict;
47 | NSString *appName = 0;
48 |
49 | /* Determine the application name */
50 | dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
51 | if (dict)
52 | appName = [dict objectForKey: @"CFBundleName"];
53 |
54 | if (![appName length])
55 | appName = [[NSProcessInfo processInfo] processName];
56 |
57 | return appName;
58 | }
59 |
60 | #if SDL_USE_NIB_FILE
61 | /* A helper category for NSString */
62 | @interface NSString (ReplaceSubString)
63 | - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
64 | @end
65 | #endif
66 |
67 | @interface SDLApplication : NSApplication
68 | @end
69 |
70 | @implementation SDLApplication
71 | /* Invoked from the Quit menu item */
72 | - (void)terminate:(id)sender
73 | {
74 | /* Post a SDL_QUIT event */
75 | SDL_Event event;
76 | event.type = SDL_QUIT;
77 | SDL_PushEvent(&event);
78 | }
79 | @end
80 |
81 | /* The main class of the application, the application's delegate */
82 | @implementation SDLMain
83 |
84 | /* Set the working directory to the .app's parent directory */
85 | - (void) setupWorkingDirectory:(BOOL)shouldChdir
86 | {
87 | if (shouldChdir)
88 | {
89 | char parentdir[MAXPATHLEN];
90 | CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
91 | CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
92 | if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) {
93 | assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */
94 | }
95 | CFRelease(url);
96 | CFRelease(url2);
97 | }
98 |
99 | }
100 |
101 | #if SDL_USE_NIB_FILE
102 |
103 | /* Fix menu to contain the real app name instead of "SDL App" */
104 | - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
105 | {
106 | NSRange aRange;
107 | NSEnumerator *enumerator;
108 | NSMenuItem *menuItem;
109 |
110 | aRange = [[aMenu title] rangeOfString:@"SDL App"];
111 | if (aRange.length != 0)
112 | [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
113 |
114 | enumerator = [[aMenu itemArray] objectEnumerator];
115 | while ((menuItem = [enumerator nextObject]))
116 | {
117 | aRange = [[menuItem title] rangeOfString:@"SDL App"];
118 | if (aRange.length != 0)
119 | [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
120 | if ([menuItem hasSubmenu])
121 | [self fixMenu:[menuItem submenu] withAppName:appName];
122 | }
123 | [ aMenu sizeToFit ];
124 | }
125 |
126 | #else
127 |
128 | static void setApplicationMenu(void)
129 | {
130 | /* warning: this code is very odd */
131 | NSMenu *appleMenu;
132 | NSMenuItem *menuItem;
133 | NSString *title;
134 | NSString *appName;
135 |
136 | appName = getApplicationName();
137 | appleMenu = [[NSMenu alloc] initWithTitle:@""];
138 |
139 | /* Add menu items */
140 | title = [@"About " stringByAppendingString:appName];
141 | [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
142 |
143 | [appleMenu addItem:[NSMenuItem separatorItem]];
144 |
145 | title = [@"Hide " stringByAppendingString:appName];
146 | [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
147 |
148 | menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
149 | [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
150 |
151 | [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
152 |
153 | [appleMenu addItem:[NSMenuItem separatorItem]];
154 |
155 | title = [@"Quit " stringByAppendingString:appName];
156 | [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
157 |
158 |
159 | /* Put menu into the menubar */
160 | menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
161 | [menuItem setSubmenu:appleMenu];
162 | [[NSApp mainMenu] addItem:menuItem];
163 |
164 | /* Tell the application object that this is now the application menu */
165 | [NSApp setAppleMenu:appleMenu];
166 |
167 | /* Finally give up our references to the objects */
168 | [appleMenu release];
169 | [menuItem release];
170 | }
171 |
172 | /* Create a window menu */
173 | static void setupWindowMenu(void)
174 | {
175 | NSMenu *windowMenu;
176 | NSMenuItem *windowMenuItem;
177 | NSMenuItem *menuItem;
178 |
179 | windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
180 |
181 | /* "Minimize" item */
182 | menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
183 | [windowMenu addItem:menuItem];
184 | [menuItem release];
185 |
186 | /* Put menu into the menubar */
187 | windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
188 | [windowMenuItem setSubmenu:windowMenu];
189 | [[NSApp mainMenu] addItem:windowMenuItem];
190 |
191 | /* Tell the application object that this is now the window menu */
192 | [NSApp setWindowsMenu:windowMenu];
193 |
194 | /* Finally give up our references to the objects */
195 | [windowMenu release];
196 | [windowMenuItem release];
197 | }
198 |
199 | /* Replacement for NSApplicationMain */
200 | static void CustomApplicationMain (int argc, char **argv)
201 | {
202 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
203 | SDLMain *sdlMain;
204 |
205 | /* Ensure the application object is initialised */
206 | [SDLApplication sharedApplication];
207 |
208 | #ifdef SDL_USE_CPS
209 | {
210 | CPSProcessSerNum PSN;
211 | /* Tell the dock about us */
212 | if (!CPSGetCurrentProcess(&PSN))
213 | if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
214 | if (!CPSSetFrontProcess(&PSN))
215 | [SDLApplication sharedApplication];
216 | }
217 | #endif /* SDL_USE_CPS */
218 |
219 | /* Set up the menubar */
220 | [NSApp setMainMenu:[[NSMenu alloc] init]];
221 | setApplicationMenu();
222 | setupWindowMenu();
223 |
224 | /* Create SDLMain and make it the app delegate */
225 | sdlMain = [[SDLMain alloc] init];
226 | [NSApp setDelegate:sdlMain];
227 |
228 | /* Start the main event loop */
229 | [NSApp run];
230 |
231 | [sdlMain release];
232 | [pool release];
233 | }
234 |
235 | #endif
236 |
237 |
238 | /*
239 | * Catch document open requests...this lets us notice files when the app
240 | * was launched by double-clicking a document, or when a document was
241 | * dragged/dropped on the app's icon. You need to have a
242 | * CFBundleDocumentsType section in your Info.plist to get this message,
243 | * apparently.
244 | *
245 | * Files are added to gArgv, so to the app, they'll look like command line
246 | * arguments. Previously, apps launched from the finder had nothing but
247 | * an argv[0].
248 | *
249 | * This message may be received multiple times to open several docs on launch.
250 | *
251 | * This message is ignored once the app's mainline has been called.
252 | */
253 | - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
254 | {
255 | const char *temparg;
256 | size_t arglen;
257 | char *arg;
258 | char **newargv;
259 |
260 | if (!gFinderLaunch) /* MacOS is passing command line args. */
261 | return FALSE;
262 |
263 | if (gCalledAppMainline) /* app has started, ignore this document. */
264 | return FALSE;
265 |
266 | temparg = [filename UTF8String];
267 | arglen = SDL_strlen(temparg) + 1;
268 | arg = (char *) SDL_malloc(arglen);
269 | if (arg == NULL)
270 | return FALSE;
271 |
272 | newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
273 | if (newargv == NULL)
274 | {
275 | SDL_free(arg);
276 | return FALSE;
277 | }
278 | gArgv = newargv;
279 |
280 | SDL_strlcpy(arg, temparg, arglen);
281 | gArgv[gArgc++] = arg;
282 | gArgv[gArgc] = NULL;
283 | return TRUE;
284 | }
285 |
286 |
287 | /* Called when the internal event loop has just started running */
288 | - (void) applicationDidFinishLaunching: (NSNotification *) note
289 | {
290 | int status;
291 |
292 | /* Set the working directory to the .app's parent directory */
293 | [self setupWorkingDirectory:gFinderLaunch];
294 |
295 | #if SDL_USE_NIB_FILE
296 | /* Set the main menu to contain the real app name instead of "SDL App" */
297 | [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
298 | #endif
299 |
300 | /* Hand off to main application code */
301 | gCalledAppMainline = TRUE;
302 | status = SDL_main (gArgc, gArgv);
303 |
304 | /* We're done, thank you for playing */
305 | exit(status);
306 | }
307 | @end
308 |
309 |
310 | @implementation NSString (ReplaceSubString)
311 |
312 | - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
313 | {
314 | unsigned int bufferSize;
315 | unsigned int selfLen = [self length];
316 | unsigned int aStringLen = [aString length];
317 | unichar *buffer;
318 | NSRange localRange;
319 | NSString *result;
320 |
321 | bufferSize = selfLen + aStringLen - aRange.length;
322 | buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
323 |
324 | /* Get first part into buffer */
325 | localRange.location = 0;
326 | localRange.length = aRange.location;
327 | [self getCharacters:buffer range:localRange];
328 |
329 | /* Get middle part into buffer */
330 | localRange.location = 0;
331 | localRange.length = aStringLen;
332 | [aString getCharacters:(buffer+aRange.location) range:localRange];
333 |
334 | /* Get last part into buffer */
335 | localRange.location = aRange.location + aRange.length;
336 | localRange.length = selfLen - localRange.location;
337 | [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
338 |
339 | /* Build output string */
340 | result = [NSString stringWithCharacters:buffer length:bufferSize];
341 |
342 | NSDeallocateMemoryPages(buffer, bufferSize);
343 |
344 | return result;
345 | }
346 |
347 | @end
348 |
349 |
350 |
351 | #ifdef main
352 | # undef main
353 | #endif
354 |
355 |
356 | /* Main entry point to executable - should *not* be SDL_main! */
357 | int main (int argc, char **argv)
358 | {
359 | /* Copy the arguments into a global variable */
360 | /* This is passed if we are launched by double-clicking */
361 | if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
362 | gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
363 | gArgv[0] = argv[0];
364 | gArgv[1] = NULL;
365 | gArgc = 1;
366 | gFinderLaunch = YES;
367 | } else {
368 | int i;
369 | gArgc = argc;
370 | gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
371 | for (i = 0; i <= argc; i++)
372 | gArgv[i] = argv[i];
373 | gFinderLaunch = NO;
374 | }
375 |
376 | #if SDL_USE_NIB_FILE
377 | [SDLApplication poseAsClass:[NSApplication class]];
378 | NSApplicationMain (argc, argv);
379 | #else
380 | CustomApplicationMain (argc, argv);
381 | #endif
382 | return 0;
383 | }
384 |
385 |
--------------------------------------------------------------------------------
/docs/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperlogic/riftty/a06a93f4c148f3c148e0b956b7ff4c35032d51d0/docs/screenshot.png
--------------------------------------------------------------------------------
/font/DejaVuSansMono-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperlogic/riftty/a06a93f4c148f3c148e0b956b7ff4c35032d51d0/font/DejaVuSansMono-Bold.ttf
--------------------------------------------------------------------------------
/font/DejaVuSansMono.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperlogic/riftty/a06a93f4c148f3c148e0b956b7ff4c35032d51d0/font/DejaVuSansMono.ttf
--------------------------------------------------------------------------------
/font/SourceCodePro-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperlogic/riftty/a06a93f4c148f3c148e0b956b7ff4c35032d51d0/font/SourceCodePro-Black.ttf
--------------------------------------------------------------------------------
/font/SourceCodePro-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperlogic/riftty/a06a93f4c148f3c148e0b956b7ff4c35032d51d0/font/SourceCodePro-Bold.ttf
--------------------------------------------------------------------------------
/font/SourceCodePro-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperlogic/riftty/a06a93f4c148f3c148e0b956b7ff4c35032d51d0/font/SourceCodePro-Regular.ttf
--------------------------------------------------------------------------------
/gen_shaders.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/ruby
2 |
3 | # I hate writting OpenGL shader glue code in C++, so I wrote this.
4 | # the input is a set of uniforms and attribs
5 | # the output is a c++ class that will:
6 | # 1) provide a nice interface to get and set these values.
7 | # 2) cache the program indices for each uniform and attrib.
8 | # 3) bind the program, uniform and attribs for rendering.
9 |
10 | require('erb')
11 |
12 | def assert &block
13 | raise AssersionFailure unless yield
14 | end
15 |
16 | class Object
17 | def camelcase
18 | to_s.split('_').map() {|s| s[0,1].upcase + s[1..-1]}.join
19 | end
20 | end
21 |
22 | class String
23 | def uncapitalize
24 | self[0, 1].downcase + self[1..-1]
25 | end
26 | end
27 |
28 | $cpp_ref_type = {
29 | :float => 'float',
30 | :int => 'int',
31 | :vec2 => 'const Vector2f&',
32 | :vec3 => 'const Vector3f&',
33 | :vec4 => 'const Vector4f&',
34 | :mat4 => 'const Matrixf&',
35 | :sampler2D => 'int'
36 | }
37 |
38 | $cpp_decl_type = {
39 | :float => 'float',
40 | :int => 'int',
41 | :vec2 => 'Vector2f',
42 | :vec3 => 'Vector3f',
43 | :vec4 => 'Vector4f',
44 | :mat4 => 'Matrixf',
45 | :sampler2D => 'int'
46 | }
47 |
48 | $uniform_call = {
49 | :float => 'glUniform1fv',
50 | :int => 'glUniform1iv',
51 | :vec2 => 'glUniform2fv',
52 | :vec3 => 'glUniform3fv',
53 | :vec4 => 'glUniform4fv',
54 | :mat4 => 'glUniformMatrix4fv',
55 | :sampler2D => 'glUniform1iv',
56 | }
57 |
58 | $attrib_size = {
59 | :float => 1,
60 | :vec2 => 2,
61 | :vec3 => 3,
62 | :vec4 => 4,
63 | }
64 |
65 | $cpp_header = <_H
67 | #define <%= prog.name.upcase %>_H
68 |
69 | #include "shader.h"
70 | #include "abaci.h"
71 | #include
72 |
73 | class <%= prog.name.camelcase %> : public Shader
74 | {
75 | public:
76 | enum Locs {
77 | <% prog.uniforms.each do |u| %>
78 | <%= u.name.camelcase %>UniformLoc,
79 | <% end %>
80 | <% prog.attribs.each do |a| %>
81 | <%= a.name.camelcase %>AttribLoc,
82 | <% end %>
83 | NumLocs
84 | };
85 |
86 | <%= prog.name.camelcase %>() : Shader()
87 | {
88 | for (int i = 0; i < NumLocs; i++)
89 | m_locs[i] = -1;
90 | }
91 |
92 | bool compileAndLink()
93 | {
94 | return compileAndLinkFromFiles("<%= prog.vsh %>", "<%= prog.fsh %>");
95 | }
96 |
97 | void apply(const Shader* prevShader, const float* attribPtr) const
98 | {
99 | <% prog.uniforms.each do |u| %>
100 | if (m_locs[<%= u.name.camelcase %>UniformLoc] < 0)
101 | m_locs[<%= u.name.camelcase %>UniformLoc] = getUniformLoc("<%= u.name %><%= u.class == BasicType ? "" : "[0]" %>");
102 | assert(m_locs[<%= u.name.camelcase %>UniformLoc] >= 0);
103 | <% end %>
104 | <% prog.attribs.each do |a| %>
105 | if (m_locs[<%= a.name.camelcase %>AttribLoc])
106 | m_locs[<%= a.name.camelcase %>AttribLoc] = getAttribLoc("<%= a.name %>");
107 | assert(m_locs[<%= a.name.camelcase %>AttribLoc] >= 0);
108 | <% end %>
109 |
110 | Shader::apply(prevShader);
111 |
112 | <% prog.uniforms.each do |u| %>
113 | <%= prog.gen_uniform_call(u) %>
114 | <% end %>
115 | <% stride = prog.attribs.map {|a| 4 * prog.calc_attrib_size(a)}.reduce {|a, b| a + b} %>
116 | <% offset = 0 %>
117 | <% prog.attribs.each do |a| %>
118 | <%= prog.gen_attrib_call(a, offset, stride) %>
119 | <% offset += prog.calc_attrib_size(a) %>
120 | <% end %>
121 | }
122 |
123 | // uniform accessors
124 | <% prog.uniforms.each do |u| %>
125 | <%= prog.uniform_ref_type(u) %> get<%= u.name.camelcase %>() const { return m_<%= u.name.camelcase.uncapitalize %>; }
126 | void set<%= u.name.camelcase %>(<%= prog.uniform_ref_type(u) %> v) { m_<%= u.name.camelcase.uncapitalize %> = v; }
127 | <% end %>
128 |
129 | protected:
130 | mutable int m_locs[NumLocs];
131 | <% prog.uniforms.each do |u| %>
132 | <%= prog.uniform_decl_type(u) %> m_<%= u.name.camelcase.uncapitalize %>;
133 | <% end %>
134 |
135 | };
136 |
137 | #endif // #define <%= prog.name.upcase %>_H
138 | CODE
139 |
140 | BasicType = Struct.new(:type, :name)
141 | ArrayType = Struct.new(:type, :name, :count)
142 |
143 | class Prog < Struct.new(:name, :vsh, :fsh, :uniforms, :attribs)
144 | def build
145 | filename = "#{name.camelcase}.h"
146 |
147 | # writable
148 | File.chmod(0666, filename) if File.exists?(filename)
149 |
150 | File.open(filename, "w") do |f|
151 | prog = self
152 | erb = ERB.new($cpp_header, 0, "<>")
153 | f.write erb.result(binding)
154 | end
155 |
156 | # readonly
157 | File.chmod(0444, filename)
158 | end
159 |
160 | def uniform_to_tex_stage uniform
161 | assert {uniform.class == BasicType}
162 | textures = uniforms.find_all {|u| u.type == :sampler2D}
163 | textures.find_index {|u| u.name == uniform.name}
164 | end
165 |
166 | def gen_uniform_call(u)
167 | loc = "m_locs[#{u.name.camelcase}UniformLoc]"
168 | count = u.class == BasicType ? 1 : u.count
169 | value = "m_#{u.name.camelcase.uncapitalize}"
170 | assert {$uniform_call[u.type]}
171 |
172 | case u.type
173 | when :int
174 | if u.class == BasicType
175 | "#{$uniform_call[u.type]}(#{loc}, #{count}, (int*){value});"
176 | else
177 | "#{$uniform_call[u.type]}(#{loc}, #{count}, (int*){value}[0]);"
178 | end
179 | when :sampler2D
180 | stage = uniform_to_tex_stage(u)
181 | assert {stage && u.class == BasicType} # ArrayType not supported on samplers!
182 | "glUniform1i(#{loc}, #{stage}); glActiveTexture(GL_TEXTURE0 + #{stage}); glBindTexture(GL_TEXTURE_2D, #{value});"
183 | when :mat4
184 | if u.class == BasicType
185 | "#{$uniform_call[u.type]}(#{loc}, #{count}, false, (float*){value});"
186 | else
187 | "#{$uniform_call[u.type]}(#{loc}, #{count}, false, (float*){value}[0]);"
188 | end
189 | else
190 | if u.class == BasicType
191 | "#{$uniform_call[u.type]}(#{loc}, #{count}, (float*){value});"
192 | else
193 | "#{$uniform_call[u.type]}(#{loc}, #{count}, (float*){value}[0]);"
194 | end
195 | end
196 | end
197 |
198 | def gen_attrib_call(a, offset, stride)
199 | assert {a.class == BasicType} # array types not supported on attribs
200 | assert {a && offset && stride}
201 |
202 | size = calc_attrib_size(a)
203 | assert {size}
204 |
205 | loc = "m_locs[#{a.name.camelcase}AttribLoc]"
206 | "glVertexAttribPointer(#{loc}, #{size}, GL_FLOAT, false, #{stride}, attribPtr + #{offset});"
207 | end
208 |
209 | def calc_attrib_size(a)
210 | assert {a.class == BasicType} # array types not supported on attribs
211 | assert {a}
212 | size = $attrib_size[a.type]
213 | assert {size}
214 | size
215 | end
216 |
217 | def uniform_decl_type(u)
218 | if u.class == BasicType
219 | $cpp_decl_type[u.type]
220 | else
221 | "std::vector<#{$cpp_decl_type[u.type]}>"
222 | end
223 | end
224 |
225 | def uniform_ref_type(u)
226 | if u.class == BasicType
227 | $cpp_ref_type[u.type]
228 | else
229 | "const std::vector<#{$cpp_decl_type[u.type]}>&"
230 | end
231 | end
232 | end
233 |
234 | progs = [Prog.new("fullbright_shader",
235 | "shader/fullbright.vsh",
236 | "shader/fullbright.fsh",
237 | [BasicType.new(:vec4, :color),
238 | BasicType.new(:mat4, :mat)],
239 | [BasicType.new(:vec3, :pos)]),
240 |
241 | Prog.new("fullbright_vert_color_shader",
242 | "shader/fullbright_vert_color.vsh",
243 | "shader/fullbright_vert_color.fsh",
244 | [BasicType.new(:mat4, :mat)],
245 | [BasicType.new(:vec3, :pos),
246 | BasicType.new(:vec4, :color)]),
247 |
248 | Prog.new("fullbright_textured_shader",
249 | "shader/fullbright_textured.vsh",
250 | "shader/fullbright_textured.fsh",
251 | [BasicType.new(:vec4, :color),
252 | BasicType.new(:mat4, :mat),
253 | BasicType.new(:sampler2D, :tex)],
254 | [BasicType.new(:vec3, :pos),
255 | BasicType.new(:vec2, :uv)]),
256 |
257 | Prog.new("fullbright_textured_text_shader",
258 | "shader/fullbright_textured.vsh",
259 | "shader/fullbright_textured_text.fsh",
260 | [BasicType.new(:vec4, :color),
261 | BasicType.new(:mat4, :mat),
262 | BasicType.new(:sampler2D, :tex),
263 | BasicType.new(:float, :lod_bias)],
264 | [BasicType.new(:vec3, :pos),
265 | BasicType.new(:vec2, :uv)]),
266 |
267 | Prog.new("fullbright_textured_vert_color_shader",
268 | "shader/fullbright_textured_vert_color.vsh",
269 | "shader/fullbright_textured_vert_color.fsh",
270 | [BasicType.new(:mat4, :mat),
271 | BasicType.new(:sampler2D, :tex)],
272 | [BasicType.new(:vec3, :pos),
273 | BasicType.new(:vec2, :uv),
274 | BasicType.new(:vec4, :color)]),
275 |
276 | Prog.new("fullbright_textured_vert_color_text_shader",
277 | "shader/fullbright_textured_vert_color.vsh",
278 | "shader/fullbright_textured_vert_color_text.fsh",
279 | [BasicType.new(:mat4, :mat),
280 | BasicType.new(:sampler2D, :tex),
281 | BasicType.new(:float, :lod_bias)],
282 | [BasicType.new(:vec3, :pos),
283 | BasicType.new(:vec2, :uv),
284 | BasicType.new(:vec4, :color)]),
285 |
286 | Prog.new("phong_textured_shader",
287 | "shader/phong_textured.vsh",
288 | "shader/phong_textured.fsh",
289 | [BasicType.new(:vec4, :color),
290 | BasicType.new(:mat4, :full_mat),
291 | BasicType.new(:mat4, :world_mat),
292 | BasicType.new(:mat4, :world_normal_mat),
293 | BasicType.new(:sampler2D, :tex),
294 | BasicType.new(:int, :num_lights),
295 | ArrayType.new(:vec3, :light_world_pos, 8),
296 | ArrayType.new(:vec3, :light_color, 8),
297 | ArrayType.new(:float, :light_strength, 8)],
298 | [BasicType.new(:vec3, :pos),
299 | BasicType.new(:vec2, :uv),
300 | BasicType.new(:vec3, :normal)])
301 | ]
302 |
303 | progs.each do |prog|
304 | prog.build
305 | end
306 |
--------------------------------------------------------------------------------
/joystick.cpp:
--------------------------------------------------------------------------------
1 | #include "joystick.h"
2 | #include
3 | #include
4 |
5 | SDL_Joystick* s_sdlJoy = 0;
6 | Joystick s_joy;
7 |
8 | void JOYSTICK_Init()
9 | {
10 | // Only Xbox360Controller is supported
11 | if (SDL_NumJoysticks() > 0)
12 | {
13 | s_sdlJoy = SDL_JoystickOpen(0);
14 |
15 | printf("Found joystick \"%s\"\n", SDL_JoystickName(0));
16 | printf(" numAxes = %d\n", SDL_JoystickNumAxes(s_sdlJoy));
17 | printf(" numButtons = %d\n", SDL_JoystickNumButtons(s_sdlJoy));
18 | printf(" numBalls = %d\n", SDL_JoystickNumBalls(s_sdlJoy));
19 | printf(" numHats = %d\n", SDL_JoystickNumHats(s_sdlJoy));
20 | }
21 | else
22 | printf("Joystick not found.\n");
23 | }
24 |
25 | void JOYSTICK_Shutdown()
26 | {
27 | if (s_sdlJoy)
28 | SDL_JoystickClose(s_sdlJoy);
29 | }
30 |
31 | void JOYSTICK_ClearFlags()
32 | {
33 | s_joy.buttonPressFlags = 0;
34 | s_joy.buttonReleaseFlags = 0;
35 | }
36 |
37 | // TODO: do a real joystick config.
38 | // maps event to axes
39 | #ifdef DARWIN
40 | static int s_axisMap[] = {
41 | Joystick::LeftStickX,
42 | Joystick::LeftStickY,
43 | Joystick::LeftTrigger,
44 | Joystick::RightStickX,
45 | Joystick::RightStickY,
46 | Joystick::RightTrigger
47 | };
48 | #else
49 | static int s_axisMap[] = {
50 | Joystick::LeftStickX,
51 | Joystick::LeftStickY,
52 | Joystick::LeftTrigger,
53 | Joystick::RightStickY,
54 | Joystick::RightStickX,
55 | Joystick::RightTrigger
56 | };
57 | #endif
58 |
59 | void JOYSTICK_UpdateMotion(const SDL_JoyAxisEvent* event)
60 | {
61 | if (event->which != 0)
62 | return;
63 |
64 | const float kDeadSpot = 0.2f;
65 |
66 | int i = s_axisMap[event->axis];
67 | switch (i)
68 | {
69 | case Joystick::LeftStickX:
70 | case Joystick::RightStickX:
71 | s_joy.axes[i] = event->value / 32768.0f;
72 | if (fabs(s_joy.axes[i]) < kDeadSpot)
73 | s_joy.axes[i] = 0.0f;
74 | break;
75 |
76 | // flip y axis, not sure if this a driver thing or an SDL thing, but I want my +y to mean up.
77 | case Joystick::LeftStickY:
78 | case Joystick::RightStickY:
79 | s_joy.axes[i] = event->value / -32768.0f;
80 | if (fabs(s_joy.axes[i]) < kDeadSpot)
81 | s_joy.axes[i] = 0.0f;
82 | break;
83 |
84 | // I want to map the triggers from 0 to 1
85 | case Joystick::LeftTrigger:
86 | case Joystick::RightTrigger:
87 | s_joy.axes[i] = ((event->value / 32768.0f) * 0.5f) + 0.5f;
88 | if (s_joy.axes[i] < kDeadSpot)
89 | s_joy.axes[i] = 0.0f;
90 | break;
91 | }
92 | }
93 |
94 | unsigned int UpdateButtonFlag(unsigned int orig, unsigned int mask, bool newState)
95 | {
96 | if (newState)
97 | return orig | mask; // set flag
98 | else
99 | return orig & ~mask; // clear flag
100 | }
101 |
102 | // TODO: do a real joystick config.
103 | #ifdef DARWIN
104 | static int s_buttonMap[] = {Joystick::DPadDown, Joystick::DPadUp, Joystick::DPadLeft, Joystick::DPadRight,
105 | Joystick::Start, Joystick::Back, Joystick::LeftStick, Joystick::RightStick,
106 | Joystick::LeftBumper, Joystick::RightBumper, Joystick::Xbox, Joystick::A,
107 | Joystick::B, Joystick::X, Joystick::Y};
108 | #else
109 | static int s_buttonMap[] = {Joystick::A, Joystick::B, Joystick::X, Joystick::Y,
110 | Joystick::LeftBumper, Joystick::RightBumper, Joystick::Back, Joystick::Start,
111 | Joystick::LeftStick, Joystick::RightStick};
112 | #endif
113 |
114 | void JOYSTICK_UpdateButton(const SDL_JoyButtonEvent* event)
115 | {
116 | if (event->which != 0)
117 | return;
118 |
119 | int i = s_buttonMap[event->button];
120 |
121 | s_joy.buttonStateFlags = UpdateButtonFlag(s_joy.buttonStateFlags, 1 << i, event->state);
122 |
123 | if (event->state)
124 | s_joy.buttonPressFlags = UpdateButtonFlag(s_joy.buttonPressFlags, 1 << i, event->state);
125 | else
126 | s_joy.buttonReleaseFlags = UpdateButtonFlag(s_joy.buttonReleaseFlags, 1 << i, event->state);
127 | }
128 |
129 | Joystick* JOYSTICK_GetJoystick()
130 | {
131 | return &s_joy;
132 | }
133 |
--------------------------------------------------------------------------------
/joystick.h:
--------------------------------------------------------------------------------
1 | #ifndef __JOYSTICK_H__
2 | #define __JOYSTICK_H__
3 |
4 | #include
5 | #include "abaci.h"
6 |
7 | struct SDL_JoyAxisEvent;
8 | struct SDL_JoyButtonEvent;
9 |
10 | // based on a Xbox360 Controller
11 | struct Joystick
12 | {
13 | Joystick() { memset(this, sizeof(Joystick), 0); }
14 |
15 | enum Axes
16 | {
17 | LeftStickX = 0,
18 | LeftStickY,
19 | RightStickX,
20 | RightStickY,
21 | LeftTrigger,
22 | RightTrigger,
23 | NumAxes
24 | };
25 |
26 | float axes[NumAxes];
27 |
28 | enum ButtonFlags
29 | {
30 | DPadDown = 0,
31 | DPadUp,
32 | DPadLeft,
33 | DPadRight,
34 | Start,
35 | Back,
36 | LeftStick,
37 | RightStick,
38 | LeftBumper,
39 | RightBumper,
40 | Xbox,
41 | A,
42 | B,
43 | X,
44 | Y,
45 | NumButtons
46 | };
47 |
48 | unsigned int buttonStateFlags;
49 | unsigned int buttonPressFlags;
50 | unsigned int buttonReleaseFlags;
51 | };
52 |
53 | void JOYSTICK_Init();
54 | void JOYSTICK_Shutdown();
55 |
56 | void JOYSTICK_ClearFlags();
57 | void JOYSTICK_UpdateMotion(const SDL_JoyAxisEvent* event);
58 | void JOYSTICK_UpdateButton(const SDL_JoyButtonEvent* event);
59 |
60 | Joystick* JOYSTICK_GetJoystick();
61 |
62 | #endif
63 |
--------------------------------------------------------------------------------
/keyboard.cpp:
--------------------------------------------------------------------------------
1 | #include "keyboard.h"
2 | #include
3 | #include
4 |
5 | extern "C" {
6 | #include "config.h"
7 | #include "child.h"
8 | #include "term.h"
9 | }
10 |
11 | void KeyboardInit()
12 | {
13 | ;
14 | }
15 |
16 | static char SymToAscii(int sym, int mod)
17 | {
18 | if (sym < 128)
19 | {
20 | char ascii = sym;
21 | if (mod & KMOD_SHIFT)
22 | {
23 | if (ascii >= 'a' && ascii <= 'z')
24 | ascii = 'A' + (ascii - 'a');
25 | else
26 | {
27 | switch (ascii)
28 | {
29 | case '`': ascii = '~'; break;
30 | case '1': ascii = '!'; break;
31 | case '2': ascii = '@'; break;
32 | case '3': ascii = '#'; break;
33 | case '4': ascii = '$'; break;
34 | case '5': ascii = '%'; break;
35 | case '6': ascii = '^'; break;
36 | case '7': ascii = '&'; break;
37 | case '8': ascii = '*'; break;
38 | case '9': ascii = '('; break;
39 | case '0': ascii = ')'; break;
40 | case '-': ascii = '_'; break;
41 | case '=': ascii = '+'; break;
42 | case '[': ascii = '{'; break;
43 | case ']': ascii = '}'; break;
44 | case '\\': ascii = '|'; break;
45 | case ';': ascii = ':'; break;
46 | case '\'': ascii = '\"'; break;
47 | case ',': ascii = '<'; break;
48 | case '.': ascii = '>'; break;
49 | case '/': ascii = '?'; break;
50 | default:
51 | break;
52 | }
53 | }
54 | }
55 | return ascii;
56 | }
57 |
58 | return 0;
59 | }
60 |
61 | struct ModState {
62 | bool numlock;
63 | bool shift;
64 | bool lalt;
65 | bool ralt;
66 | bool alt;
67 | bool lmeta;
68 | bool rmeta;
69 | bool meta;
70 | bool lctrl;
71 | bool rctrl;
72 | bool ctrl;
73 | bool ctrl_lalt_altgr;
74 | bool altgr;
75 | int mods;
76 | };
77 |
78 | static void ss3(char c)
79 | {
80 | char buf[3];
81 | int len = 0;
82 | buf[len++] = '\e';
83 | buf[len++] = 'O';
84 | buf[len++] = c;
85 | assert(len == 3);
86 | child_send(buf, len);
87 | }
88 |
89 | static void csi(char c)
90 | {
91 | char buf[3];
92 | int len = 0;
93 | buf[len++] = '\e';
94 | buf[len++] = '[';
95 | buf[len++] = c;
96 | assert(len == 3);
97 | child_send(buf, len);
98 | }
99 |
100 | static void mod_csi(ModState mod_state, char c)
101 | {
102 | char buf[32];
103 | int len = 0;
104 | len = sprintf(buf, "\e[1;%c%c", mod_state.mods + '1', c);
105 | child_send(buf, len);
106 | }
107 |
108 | static void mod_ss3(ModState mod_state, char c)
109 | {
110 | if (mod_state.mods)
111 | mod_csi(mod_state, c);
112 | else
113 | ss3(c);
114 | }
115 |
116 | static void tilde_code(ModState mod_state, uchar code)
117 | {
118 | char buf[32];
119 | int len = sprintf(buf, mod_state.mods ? "\e[%i;%c~" : "\e[%i~", code, mod_state.mods + '1');
120 | child_send(buf, len);
121 | }
122 |
123 | static void cursor_key(ModState mod_state, char code, char symbol)
124 | {
125 | if (mod_state.mods)
126 | mod_csi(mod_state, code);
127 | else if (term.app_cursor_keys)
128 | ss3(code);
129 | else
130 | csi(code);
131 | }
132 |
133 | static void ch(char c)
134 | {
135 | child_send(&c, 1);
136 | }
137 |
138 | static void esc_if(bool b)
139 | {
140 | if (b)
141 | ch('\e');
142 | }
143 |
144 | void ctrl_ch(ModState mod_state, uchar c)
145 | {
146 | esc_if(mod_state.alt);
147 | if (mod_state.shift)
148 | {
149 | /*
150 | // Send C1 control char if the charset supports it.
151 | // Otherwise prefix the C0 char with ESC.
152 | if (c < 0x20) {
153 | wchar wc = c | 0x80;
154 | int l = cs_wcntombn(buf + len, &wc, cs_cur_max, 1);
155 | if (l > 0 && buf[len] != '?') {
156 | len += l;
157 | return;
158 | }
159 | };
160 | */
161 | esc_if(!mod_state.alt);
162 | }
163 | ch(c);
164 | }
165 |
166 | #define CDEL 0x0007f
167 |
168 | bool ProcessKeyEvent(SDL_KeyboardEvent* key)
169 | {
170 | bool down = (key->type == SDL_KEYDOWN);
171 | int sym = key->keysym.sym;
172 | int mod = key->keysym.mod;
173 | char ascii = SymToAscii(sym, mod);
174 |
175 | // init modState struct
176 | ModState mod_state;
177 | mod_state.numlock = (mod & KMOD_NUM) != 0;
178 | mod_state.shift = (mod & (KMOD_LSHIFT | KMOD_RSHIFT)) != 0;
179 | if (cfg.swap_alt_and_meta_keys) {
180 | mod_state.lalt = (mod & KMOD_LGUI) != 0;
181 | mod_state.ralt = (mod & KMOD_RGUI) != 0;
182 | mod_state.lmeta = (mod & KMOD_LALT) != 0;
183 | mod_state.rmeta = (mod & KMOD_RALT) != 0;
184 | } else {
185 | mod_state.lalt = (mod & KMOD_LALT) != 0;
186 | mod_state.ralt = (mod & KMOD_RALT) != 0;
187 | mod_state.lmeta = (mod & KMOD_LGUI) != 0;
188 | mod_state.rmeta = (mod & KMOD_RGUI) != 0;
189 | }
190 | mod_state.alt = mod_state.lalt || mod_state.ralt;
191 | mod_state.meta = mod_state.lmeta || mod_state.rmeta;
192 | mod_state.lctrl = (mod & KMOD_LCTRL) != 0;
193 | mod_state.rctrl = (mod & KMOD_RCTRL) != 0;
194 | mod_state.ctrl = mod_state.lctrl || mod_state.rctrl;
195 | mod_state.ctrl_lalt_altgr = cfg.ctrl_alt_is_altgr & mod_state.ctrl & mod_state.lalt & !mod_state.ralt,
196 | mod_state.altgr = mod_state.ralt | mod_state.ctrl_lalt_altgr;
197 | mod_state.mods = mod_state.shift * MDK_SHIFT | mod_state.alt * MDK_ALT | mod_state.ctrl * MDK_CTRL;
198 |
199 | // check for magic quit (meta + esc)
200 | if (down && sym == SDLK_ESCAPE && mod_state.meta) {
201 | return true;
202 | }
203 |
204 | if (down) {
205 | switch (sym) {
206 | case SDLK_RETURN:
207 | if (!mod_state.ctrl) {
208 | esc_if(mod_state.alt);
209 | if (term.newline_mode) {
210 | ch('\r');
211 | ch('\n');
212 | } else {
213 | ch(mod_state.shift ? '\n' : '\r');
214 | }
215 | } else {
216 | ctrl_ch(mod_state, CTRL('^'));
217 | }
218 | break;
219 | case SDLK_BACKSPACE:
220 | if (!mod_state.ctrl) {
221 | esc_if(mod_state.alt);
222 | ch(term.backspace_sends_bs ? '\b' : CDEL);
223 | } else {
224 | ctrl_ch(mod_state, term.backspace_sends_bs ? CDEL : CTRL('_'));
225 | }
226 | break;
227 | case SDLK_TAB:
228 | if (!mod_state.alt && !mod_state.ctrl) {
229 | mod_state.shift ? csi('Z') : ch('\t');
230 | }
231 | break;
232 | case SDLK_ESCAPE:
233 | if (term.app_escape_key)
234 | ss3('[');
235 | else
236 | ctrl_ch(mod_state, term.escape_sends_fs ? CTRL('\\') : CTRL('['));
237 | break;
238 | case SDLK_PAUSE:
239 | ctrl_ch(mod_state, mod_state.ctrl ? CTRL('\\') : CTRL(']'));
240 | break;
241 | case SDLK_F1:
242 | case SDLK_F2:
243 | case SDLK_F3:
244 | case SDLK_F4:
245 | case SDLK_F5:
246 | case SDLK_F6:
247 | case SDLK_F7:
248 | case SDLK_F8:
249 | case SDLK_F9:
250 | case SDLK_F10:
251 | case SDLK_F11:
252 | case SDLK_F12:
253 | case SDLK_F13:
254 | case SDLK_F14:
255 | case SDLK_F15:
256 | // TODO:
257 | /*
258 | if (term.vt220_keys && ctrl && VK_F3 <= key && key <= VK_F10)
259 | key += 10, mods &= ~MDK_CTRL;
260 | if (key <= VK_F4)
261 | mod_ss3(key - VK_F1 + 'P');
262 | else {
263 | tilde_code(
264 | (uchar[]){
265 | 15, 17, 18, 19, 20, 21, 23, 24, 25, 26,
266 | 28, 29, 31, 32, 33, 34, 42, 43, 44, 45
267 | }[key - VK_F5]
268 | );
269 | }
270 | */
271 | break;
272 | case SDLK_INSERT:
273 | tilde_code(mod_state, 2);
274 | break;
275 | case SDLK_DELETE:
276 | tilde_code(mod_state, 3);
277 | break;
278 | case SDLK_HOME:
279 | if (term.vt220_keys)
280 | tilde_code(mod_state, 1);
281 | else
282 | cursor_key(mod_state, 'H', '7');
283 | break;
284 | case SDLK_END:
285 | if (term.vt220_keys)
286 | tilde_code(mod_state, 4);
287 | else
288 | cursor_key(mod_state, 'F', '1');
289 | break;
290 | case SDLK_KP_0:
291 | case SDLK_KP_1:
292 | case SDLK_KP_2:
293 | case SDLK_KP_3:
294 | case SDLK_KP_4:
295 | case SDLK_KP_5:
296 | case SDLK_KP_6:
297 | case SDLK_KP_7:
298 | case SDLK_KP_8:
299 | case SDLK_KP_9:
300 | // TODO:
301 | break;
302 | case SDLK_KP_PERIOD:
303 | // TODO:
304 | break;
305 | case SDLK_KP_DIVIDE:
306 | // TODO:
307 | break;
308 | case SDLK_KP_MULTIPLY:
309 | // TODO:
310 | break;
311 | case SDLK_KP_MINUS:
312 | // TODO:
313 | break;
314 | case SDLK_KP_PLUS:
315 | // TODO:
316 | break;
317 | case SDLK_KP_ENTER:
318 | mod_ss3(mod_state, 'M');
319 | break;
320 | case SDLK_KP_EQUALS:
321 | // TODO:
322 | break;
323 | case SDLK_UP:
324 | cursor_key(mod_state, 'A', '8');
325 | break;
326 | case SDLK_DOWN:
327 | cursor_key(mod_state, 'B', '2');
328 | break;
329 | case SDLK_RIGHT:
330 | cursor_key(mod_state, 'C', '6');
331 | break;
332 | case SDLK_LEFT:
333 | cursor_key(mod_state, 'D', '4');
334 | break;
335 | case SDLK_CLEAR:
336 | cursor_key(mod_state, 'E', '5');
337 | break;
338 | case SDLK_LSHIFT:
339 | case SDLK_RSHIFT:
340 | break;
341 | case SDLK_LCTRL:
342 | case SDLK_RCTRL:
343 | break;
344 | case SDLK_LALT:
345 | case SDLK_RALT:
346 | break;
347 | case SDLK_LGUI:
348 | case SDLK_RGUI:
349 | break;
350 | default: {
351 | if (mod_state.ctrl) {
352 | ctrl_ch(mod_state, CTRL(ascii));
353 | } else {
354 | esc_if(mod_state.alt);
355 | ch(ascii);
356 | }
357 | break;
358 | }
359 | }
360 | }
361 | return false; // don't quit
362 | }
363 |
--------------------------------------------------------------------------------
/keyboard.h:
--------------------------------------------------------------------------------
1 | #ifndef KEYBOARD_H
2 | #define KEYBOARD_H
3 |
4 | #include
5 |
6 | void KeyboardInit();
7 |
8 | // return true for magic quit event (meta + esc)
9 | bool ProcessKeyEvent(SDL_KeyboardEvent* key);
10 |
11 | #endif
12 |
--------------------------------------------------------------------------------
/kill.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/ruby
2 |
3 | # kill all running riftty processes
4 |
5 | `ps -la`.each_line do |line|
6 | tokens = line.split(/\s+/)
7 | pid = tokens[2]
8 | cmd = tokens[15]
9 | if (tokens[15] == './riftty')
10 | `kill -9 #{pid}`
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/mintty/README.md:
--------------------------------------------------------------------------------
1 | mintty
2 | =======================
3 | This is a modified version of the mintty terminal emulator program for cygwin and msys.
4 |
5 | http://code.google.com/p/mintty/
6 |
7 | Here are some of the changes:
8 | * stubbed out win calls
9 | * allowed headers to be included within C++ projects.
10 | * renamed variables, typedefs & macros which use C++ reserved words. (class, delete, new, or)
11 | * renamed some typedefs / macros to not confliced with existing C++ types. (string)
12 | * commented or ifdef'ed out windows dependencies.
13 | * reformatted code from 2 space indents to 4.
14 | * commented out, deleted or ifdefed out some cygwin dependencies.
15 |
--------------------------------------------------------------------------------
/mintty/charset.c:
--------------------------------------------------------------------------------
1 | // charset.c (part of mintty)
2 | // Copyright 2008-11 Andy Koppe
3 | // Based on code from PuTTY-0.60 by Simon Tatham and team.
4 | // Licensed under the terms of the GNU General Public License v3 or later.
5 |
6 | #include "charset.h"
7 |
8 | #include "config.h"
9 |
10 | #if HAS_LOCALES
11 | #include
12 | #include
13 | #endif
14 |
15 | //#include
16 | //#include
17 |
18 | static cs_mode mode = CSM_DEFAULT;
19 |
20 | static mintty_string default_locale; // Used unless UTF-8 or ACP mode is on.
21 |
22 | static mintty_string term_locale; // Locale set via terminal control sequences.
23 | static mintty_string config_locale; // Locale configured in the options.
24 | static mintty_string env_locale; // Locale determined by the environment.
25 | #if HAS_LOCALES
26 | static bool valid_default_locale, use_locale;
27 | bool cs_ambig_wide;
28 | #endif
29 |
30 | static uint codepage, default_codepage;
31 |
32 | static wchar cp_default_wchar;
33 | static char cp_default_char[4];
34 |
35 | int cs_cur_max;
36 |
37 | static const struct {
38 | ushort cp;
39 | mintty_string name;
40 | }
41 |
42 | #ifndef CP_UTF8
43 | #define CP_UTF8 65001
44 | #endif
45 |
46 | #ifndef CP_ACP
47 | #define CP_ACP 0
48 | #endif
49 |
50 | #ifndef CP_OEMCP
51 | #define CP_OEMCP 1
52 | #endif
53 |
54 | cs_names[] = {
55 | { CP_UTF8, "UTF-8"},
56 | { CP_UTF8, "UTF8"},
57 | { 20127, "ASCII"},
58 | { 20127, "US-ASCII"},
59 | { 20127, "ANSI_X3.4-1968"},
60 | { 20866, "KOI8-R"},
61 | { 20866, "KOI8R"},
62 | { 20866, "KOI8"},
63 | { 21866, "KOI8-U"},
64 | { 21866, "KOI8U"},
65 | { 20932, "eucJP"},
66 | { 20932, "EUC-JP"},
67 | { 874, "CP874"},
68 | { 874, "TIS620"},
69 | { 874, "TIS-620"},
70 | { 932, "SJIS"},
71 | { 932, "shift_jis"},
72 | { 936, "GBK"},
73 | { 936, "GB2312"},
74 | { 936, "eucCN"},
75 | { 936, "EUC-CN"},
76 | { 949, "eucKR"},
77 | { 949, "EUC-KR"},
78 | { 950, "Big5"},
79 | // Not (yet) supported by Cygwin
80 | { 1361, "JOHAB"}, // Korean
81 | { 1361, "KSC5601"},
82 | { 54936, "GB18030"},
83 | { CP_ACP, "ANSI"},
84 | { CP_ACP, "ACP"},
85 | {CP_OEMCP, "OEM"},
86 | {CP_OEMCP, "OCP"},
87 | };
88 |
89 | static const struct {
90 | ushort cp;
91 | mintty_string desc;
92 | }
93 | cs_descs[] = {
94 | { CP_UTF8, "Unicode"},
95 | { 28591, "Western European"},
96 | { 28592, "Central European"},
97 | { 28593, "South European"},
98 | { 28594, "North European"},
99 | { 28595, "Cyrillic"},
100 | { 28596, "Arabic"},
101 | { 28597, "Greek"},
102 | { 28598, "Hebrew"},
103 | { 28599, "Turkish"},
104 | #if HAS_LOCALES
105 | { 28600, "Nordic"},
106 | { 28601, "Thai"},
107 | #endif
108 | { 28603, "Baltic"},
109 | #if HAS_LOCALES
110 | { 28604, "Celtic"},
111 | #endif
112 | { 28605, "\"euro\""},
113 | #if HAS_LOCALES
114 | { 28606, "Balkans"},
115 | #endif
116 | { 20866, "Russian"},
117 | { 21866, "Ukrainian"},
118 | { 936, "Chinese"},
119 | { 950, "Chinese"},
120 | { 932, "Japanese"},
121 | #if HAS_LOCALES
122 | { 20932, "Japanese"},
123 | #endif
124 | { 949, "Korean"},
125 | };
126 |
127 | mintty_string locale_menu[8];
128 | mintty_string charset_menu[lengthof(cs_descs) + 4];
129 |
130 | static void
131 | strtoupper(char *dst, mintty_string src)
132 | {
133 | while ((*dst++ = toupper((uchar)*src++)));
134 | }
135 |
136 | // Return the charset name for a codepage number.
137 | static mintty_string
138 | cs_name(uint cp)
139 | {
140 | uint i;
141 | for (i = 0; i < lengthof(cs_names); i++) {
142 | if (cp == cs_names[i].cp)
143 | return cs_names[i].name;
144 | }
145 |
146 | static char buf[16];
147 | if (cp >= 28591 && cp <= 28606)
148 | sprintf(buf, "ISO-8859-%u", cp - 28590);
149 | else
150 | sprintf(buf, "CP%u", cp);
151 | return buf;
152 | }
153 |
154 | // Check whether a codepage is installed.
155 | static bool
156 | valid_codepage(uint cp)
157 | {
158 | #if 0
159 | CPINFO cpi;
160 | return GetCPInfo(cp, &cpi);
161 | #else
162 | return false;
163 | #endif
164 | }
165 |
166 | // Find the codepage number for a charset name.
167 | static uint
168 | cs_codepage(mintty_string name)
169 | {
170 | #if 0
171 | uint cp = CP_ACP;
172 | char upname[strlen(name) + 1];
173 | strtoupper(upname, name);
174 | uint iso;
175 | if (sscanf(upname, "ISO-8859-%u", &iso) == 1 ||
176 | sscanf(upname, "ISO8859-%u", &iso) == 1 ||
177 | sscanf(upname, "ISO8859%u", &iso) == 1) {
178 | if (iso && iso <= 16 && iso != 12)
179 | cp = 28590 + iso;
180 | }
181 | else if (sscanf(upname, "CP%u", &cp) == 1 ||
182 | sscanf(upname, "WIN%u", &cp) == 1 ||
183 | sscanf(upname, "%u", &cp) == 1) {
184 | // Got a codepage number.
185 | }
186 | else {
187 | // Search the charset table.
188 | uint i;
189 | for (i = 0; i < lengthof(cs_names); i++) {
190 | if (!strcasecmp(name, cs_names[i].name)) {
191 | cp = cs_names[i].cp;
192 | break;
193 | }
194 | }
195 | }
196 |
197 | return
198 | cp == CP_ACP ? GetACP() :
199 | cp == CP_OEMCP ? GetOEMCP() :
200 | valid_codepage(cp) ? cp : GetACP();
201 | #else
202 | return 0;
203 | #endif
204 | }
205 |
206 | static void
207 | init_locale_menu(void)
208 | {
209 | uint count = 0;
210 |
211 | #if 0
212 | void add_lcid(LCID lcid) {
213 | char locale[8];
214 | int lang_len = GetLocaleInfo(lcid, LOCALE_SISO639LANGNAME,
215 | locale, sizeof locale);
216 | if (!lang_len)
217 | return;
218 | if (GetLocaleInfo(lcid, LOCALE_SISO3166CTRYNAME,
219 | locale + lang_len, sizeof locale - lang_len))
220 | locale[lang_len - 1] = '_';
221 | for (uint i = 1; i < count; i++)
222 | if (!strcmp(locale, locale_menu[i]))
223 | return;
224 | locale_menu[count++] = strdup(locale);
225 | }
226 | #endif
227 |
228 | locale_menu[count++] = "(Default)";
229 | #if 0
230 | add_lcid(GetUserDefaultUILanguage());
231 | add_lcid(LOCALE_USER_DEFAULT);
232 | add_lcid(LOCALE_SYSTEM_DEFAULT);
233 | add_lcid(GetSystemDefaultUILanguage());
234 | #endif
235 | locale_menu[count++] = "C";
236 | }
237 |
238 | static void
239 | init_charset_menu(void)
240 | {
241 | charset_menu[0] = "(Default)";
242 | uint i;
243 | mintty_string *p = charset_menu + 1;
244 | for (i = 0; i < lengthof(cs_descs); i++) {
245 | uint cp = cs_descs[i].cp;
246 | if (valid_codepage(cp) || (28591 <= cp && cp <= 28606))
247 | *p++ = asform("%s (%s)", cs_name(cp), cs_descs[i].desc);
248 | }
249 | #if 0
250 | string oem_cs = cs_name(GetOEMCP());
251 | if (*oem_cs == 'C')
252 | *p++ = asform("%s (OEM codepage)", oem_cs);
253 |
254 | string ansi_cs = cs_name(GetACP());
255 | if (*ansi_cs == 'C')
256 | *p++ = asform("%s (ANSI codepage)", ansi_cs);
257 | #endif
258 | }
259 |
260 | static void
261 | get_cp_info(void)
262 | {
263 | #if 0
264 | CPINFOEXW cpi;
265 | GetCPInfoExW(codepage, 0, &cpi);
266 | cs_cur_max = cpi.MaxCharSize;
267 | cp_default_wchar = cpi.UnicodeDefaultChar;
268 | int len =
269 | WideCharToMultiByte(codepage, 0, &cp_default_wchar, 1,
270 | cp_default_char, sizeof cp_default_char - 1, 0, 0);
271 | cp_default_char[len] = 0;
272 | #endif
273 | }
274 |
275 | static void
276 | update_mode(void)
277 | {
278 | codepage =
279 | mode == CSM_UTF8 ? CP_UTF8 : mode == CSM_OEM ? 437 : default_codepage;
280 |
281 | #if HAS_LOCALES
282 | bool use_default_locale = mode == CSM_DEFAULT && valid_default_locale;
283 | setlocale(LC_CTYPE,
284 | mode == CSM_OEM ? "C.CP437" :
285 | use_default_locale ? default_locale :
286 | cs_ambig_wide ? "ja_JP.UTF-8" : "C.UTF-8"
287 | );
288 | use_locale = use_default_locale || mode == CSM_UTF8;
289 | if (use_locale)
290 | cs_cur_max = MB_CUR_MAX;
291 | else
292 | get_cp_info();
293 | #else
294 | get_cp_info();
295 | #endif
296 |
297 | // Clear output conversion state.
298 | cs_mb1towc(0, 0);
299 | }
300 |
301 | void
302 | cs_set_mode(cs_mode new_mode)
303 | {
304 | if (new_mode == mode)
305 | return;
306 | mode = new_mode;
307 | update_mode();
308 | }
309 |
310 | static void
311 | update_locale(void)
312 | {
313 | mintty_delete(default_locale);
314 |
315 | mintty_string locale = term_locale ?: config_locale ?: env_locale;
316 | mintty_string dot = strchr(locale, '.');
317 | mintty_string charset = dot ? dot + 1 : locale;
318 |
319 | #if HAS_LOCALES
320 | mintty_string set_locale = setlocale(LC_CTYPE, locale);
321 | if (!set_locale) {
322 | locale = asform("C.%s", charset);
323 | set_locale = setlocale(LC_CTYPE, locale);
324 | mintty_delete(locale);
325 | }
326 |
327 | valid_default_locale = set_locale;
328 | if (valid_default_locale) {
329 | default_codepage = cs_codepage(nl_langinfo(CODESET));
330 | default_locale = strdup(set_locale);
331 | cs_ambig_wide = wcwidth(0x3B1) == 2;
332 | }
333 | else {
334 | #endif
335 | default_codepage = cs_codepage(charset);
336 | default_locale = asform("C.%u", default_codepage);
337 | #if HAS_LOCALES
338 | cs_ambig_wide = font_ambig_wide;
339 | }
340 | #endif
341 |
342 | update_mode();
343 | }
344 |
345 | mintty_string
346 | cs_get_locale(void)
347 | {
348 | return default_locale;
349 | }
350 |
351 | void
352 | cs_set_locale(mintty_string locale)
353 | {
354 | mintty_delete(term_locale);
355 | term_locale = *locale ? strdup(locale) : 0;
356 | update_locale();
357 | }
358 |
359 | void
360 | cs_reconfig(void)
361 | {
362 | mintty_delete(config_locale);
363 | if (*cfg.locale) {
364 | config_locale =
365 | asform("%s%s%s", cfg.locale, *cfg.charset ? "." : "", cfg.charset);
366 | #if HAS_LOCALES
367 | if (setlocale(LC_CTYPE, config_locale) &&
368 | wcwidth(0x3B1) == 2 && !font_ambig_wide) {
369 | // Attach "@cjknarrow" to locale if using an ambig-narrow font
370 | // with an ambig-wide locale setting
371 | mintty_string l = config_locale;
372 | config_locale = asform("%s@cjknarrow", l);
373 | mintty_delete(l);
374 | }
375 | #endif
376 | }
377 | else
378 | config_locale = 0;
379 |
380 | update_locale();
381 | }
382 |
383 | static mintty_string
384 | getlocenv(mintty_string name)
385 | {
386 | mintty_string val = getenv(name);
387 | return val && *val ? val : 0;
388 | }
389 |
390 | void
391 | cs_init(void)
392 | {
393 | init_locale_menu();
394 | init_charset_menu();
395 |
396 | env_locale =
397 | #if HAS_LOCALES
398 | setlocale(LC_CTYPE, "") ?:
399 | #endif
400 | getlocenv("LC_ALL") ?: getlocenv("LC_CTYPE") ?: getlocenv("LANG");
401 |
402 | env_locale = env_locale ? strdup(env_locale) : "C";
403 |
404 | cs_reconfig();
405 | }
406 |
407 | mintty_string
408 | cs_lang(void)
409 | {
410 | return config_locale;
411 | }
412 |
413 | int
414 | cs_wcntombn(char *s, const wchar *ws, size_t len, size_t wlen)
415 | {
416 | #if HAS_LOCALES
417 | if (use_locale) {
418 | // The POSIX way
419 | size_t i = 0, wi = 0;
420 | len -= MB_CUR_MAX;
421 | while (wi < wlen && i <= len) {
422 | int n = wctomb(&s[i], ws[wi++]);
423 | // Drop untranslatable characters.
424 | if (n >= 0)
425 | i += n;
426 | }
427 | return i;
428 | }
429 | #endif
430 | return 0;//WideCharToMultiByte(codepage, 0, ws, wlen, s, len, 0, 0);
431 | }
432 |
433 | int
434 | cs_mbstowcs(wchar *ws, const char *s, size_t wlen)
435 | {
436 | #if HAS_LOCALES
437 | if (use_locale)
438 | return mbstowcs(ws, s, wlen);
439 | #endif
440 | return 0;//MultiByteToWideChar(codepage, 0, s, -1, ws, wlen) - 1;
441 | }
442 |
443 | int
444 | cs_mb1towc(wchar *pwc, char c)
445 | {
446 | #if HAS_LOCALES
447 | if (use_locale)
448 | return mbrtowc(pwc, &c, 1, 0);
449 | return 0;
450 | #else
451 |
452 | // The Windows way
453 | static int sn;
454 | static char s[8];
455 | static wchar ws[2];
456 |
457 | if (!pwc) {
458 | // Reset state
459 | sn = 0;
460 | return 0;
461 | }
462 | if (sn < 0) {
463 | // Leftover surrogate
464 | *pwc = ws[1];
465 | sn = 0;
466 | return 1;
467 | }
468 | s[sn++] = c;
469 | s[sn] = 0;
470 | switch (MultiByteToWideChar(codepage, 0, s, sn, ws, 2)) {
471 | when 1: {
472 | // Incomplete sequences yield the codepage's default character, but so
473 | // does the default character's very own (valid) sequence.
474 | // Pre-Vista, DBCS codepages return a null character rather
475 | // than the default character for incomplete sequences.
476 | bool incomplete =
477 | (*ws == cp_default_wchar && strcmp(s, cp_default_char)) ||
478 | (!*ws && *s);
479 | if (!incomplete) {
480 | *pwc = *ws;
481 | sn = 0;
482 | return 1;
483 | }
484 | }
485 | when 2:
486 | if (IS_HIGH_SURROGATE(*ws)) {
487 | *pwc = *ws;
488 | sn = -1; // Surrogate pair
489 | return 0;
490 | }
491 | // Special handling for GB18030. Windows considers the first two bytes
492 | // of a four-byte sequence as an encoding error followed by a digit.
493 | if (codepage == 54936 && sn == 2 && ws[1] >= '0' && ws[1] <= '9')
494 | return -2;
495 | return -1; // Encoding error
496 | }
497 | return sn < cs_cur_max ? -2 : -1;
498 | #endif
499 | }
500 |
501 | wchar
502 | cs_btowc_glyph(char c)
503 | {
504 | wchar wc = 0;
505 | //MultiByteToWideChar(codepage, MB_USEGLYPHCHARS, &c, 1, &wc, 1);
506 | return wc;
507 | }
508 |
--------------------------------------------------------------------------------
/mintty/charset.h:
--------------------------------------------------------------------------------
1 | #ifndef CHARSET_H
2 | #define CHARSET_H
3 |
4 | #include "std.h"
5 |
6 | #if 1//CYGWIN_VERSION_DLL_MAJOR >= 1007
7 | #define HAS_LOCALES 1
8 | #else
9 | #define HAS_LOCALES 0
10 | #endif
11 |
12 | static inline wchar
13 | high_surrogate(xchar xc)
14 | { return 0xD800 | (((xc - 0x10000) >> 10) & 0x3FF); }
15 |
16 | static inline wchar
17 | low_surrogate(xchar xc)
18 | { return 0xDC00 | (xc & 0x3FF); }
19 |
20 | static inline bool
21 | is_high_surrogate(wchar wc)
22 | { return (wc & 0xFC00) == 0xD800; }
23 |
24 | static inline bool
25 | is_low_surrogate(wchar wc)
26 | { return (wc & 0xFC00) == 0xDC00; }
27 |
28 | static inline xchar
29 | combine_surrogates(wchar hwc, wchar lwc)
30 | { return 0x10000 + ((hwc & 0x3FF) << 10) + (lwc & 0x3FF); }
31 |
32 | void cs_init(void);
33 | void cs_reconfig(void);
34 |
35 | mintty_string cs_lang(void);
36 |
37 | mintty_string cs_get_locale(void);
38 | void cs_set_locale(mintty_string);
39 |
40 | typedef enum { CSM_DEFAULT, CSM_OEM, CSM_UTF8 } cs_mode;
41 | void cs_set_mode(cs_mode);
42 |
43 | int cs_wcntombn(char *s, const wchar *ws, size_t len, size_t wlen);
44 | int cs_mbstowcs(wchar *ws, const char *s, size_t wlen);
45 | int cs_mb1towc(wchar *pwc, char c);
46 | wchar cs_btowc_glyph(char);
47 |
48 | extern mintty_string locale_menu[];
49 | extern mintty_string charset_menu[];
50 |
51 | extern int cs_cur_max;
52 |
53 | extern bool font_ambig_wide;
54 |
55 | #if HAS_LOCALES
56 | extern bool cs_ambig_wide;
57 | #else
58 | #define cs_ambig_wide font_ambig_wide
59 | int xcwidth(xchar c);
60 | #endif
61 |
62 | #endif
63 |
--------------------------------------------------------------------------------
/mintty/child.c:
--------------------------------------------------------------------------------
1 | // child.c (part of mintty)
2 | // Copyright 2008-11 Andy Koppe
3 | // Licensed under the terms of the GNU General Public License v3 or later.
4 |
5 | #include "child.h"
6 |
7 | #include "term.h"
8 | #include "charset.h"
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | //#include
19 |
20 | #ifdef DARWIN
21 | #include // on macos this is where forkpty is.
22 | #endif
23 |
24 | //#include
25 |
26 | #if 0//CYGWIN_VERSION_DLL_MAJOR < 1007
27 | #include
28 | #include
29 | #include
30 | #include
31 | #endif
32 |
33 | #define CDEL (0x9300)
34 |
35 | char *home, *cmd;
36 |
37 | static pid_t pid;
38 | static bool killed;
39 | static int pty_fd = -1, log_fd = -1;
40 |
41 | static void
42 | error(char *action)
43 | {
44 | char *msg;
45 | int len = asprintf(&msg, "Failed to %s: %s.", action, strerror(errno));
46 | if (len > 0) {
47 | term_write(msg, len);
48 | free(msg);
49 | }
50 | }
51 |
52 | static void
53 | sigexit(int sig)
54 | {
55 | if (pid)
56 | kill(-pid, SIGHUP);
57 | signal(sig, SIG_DFL);
58 | kill(getpid(), sig);
59 | }
60 |
61 | void
62 | child_create(const char *argv[], struct winsize *winp)
63 | {
64 | mintty_string lang = cs_lang();
65 |
66 | // xterm and urxvt ignore SIGHUP, so let's do the same.
67 | signal(SIGHUP, SIG_IGN);
68 |
69 | signal(SIGINT, sigexit);
70 | signal(SIGTERM, sigexit);
71 | signal(SIGQUIT, sigexit);
72 |
73 | /* Save real stderr so we can fprintf to it instead of pty for errors. */
74 | int STDERR = dup(STDERR_FILENO);
75 | FILE *parent_stderr = fdopen(STDERR, "w");
76 | setvbuf(parent_stderr, NULL, _IONBF, 0);
77 |
78 | // Create the child process and pseudo terminal.
79 | char pty_name[PATH_MAX];
80 | pid = forkpty(&pty_fd, pty_name, 0, winp);
81 |
82 | if (pid < 0) {
83 | pid = 0;
84 | bool rebase_prompt = (errno == EAGAIN);
85 | error("fork child process");
86 | if (rebase_prompt) {
87 | static const char msg[] =
88 | "\r\nDLL rebasing may be required. See 'rebaseall --help'.";
89 | term_write(msg, sizeof msg - 1);
90 | }
91 | term_hide_cursor();
92 | }
93 | else if (!pid) { // Child process.
94 |
95 | // Reset signals
96 | signal(SIGHUP, SIG_DFL);
97 | signal(SIGINT, SIG_DFL);
98 | signal(SIGQUIT, SIG_DFL);
99 | signal(SIGTERM, SIG_DFL);
100 | signal(SIGCHLD, SIG_DFL);
101 |
102 | // Mimick login's behavior by disabling the job control signals
103 | signal(SIGTSTP, SIG_IGN);
104 | signal(SIGTTIN, SIG_IGN);
105 | signal(SIGTTOU, SIG_IGN);
106 |
107 | setenv("TERM", cfg.term, true);
108 | fprintf(parent_stderr, "mintty: TERM = %s\n", cfg.term);
109 |
110 | if (lang) {
111 | unsetenv("LC_ALL");
112 | unsetenv("LC_COLLATE");
113 | unsetenv("LC_CTYPE");
114 | unsetenv("LC_MONETARY");
115 | unsetenv("LC_NUMERIC");
116 | unsetenv("LC_TIME");
117 | unsetenv("LC_MESSAGES");
118 | setenv("LANG", lang, true);
119 | }
120 |
121 | // Terminal line settings
122 | struct termios attr;
123 | tcgetattr(0, &attr);
124 | attr.c_cc[VERASE] = cfg.backspace_sends_bs ? CTRL('H') : CDEL;
125 | attr.c_iflag |= IXANY | IMAXBEL;
126 | attr.c_lflag |= ECHOE | ECHOK | ECHOCTL | ECHOKE;
127 | tcsetattr(0, TCSANOW, &attr);
128 |
129 | fprintf(parent_stderr, "mintty: error = %s\n", strerror(errno));
130 | fprintf(parent_stderr, "mintty: before execvp in child pid = %d\n", getpid());
131 |
132 | // Invoke command
133 | execvp(*argv, (char **)argv);
134 |
135 | // If we get here, exec failed.
136 | fprintf(parent_stderr, "%s: %s\r\n", cmd, strerror(errno));
137 |
138 | exit(255);
139 | }
140 | else { // Parent process.
141 |
142 | fprintf(stderr, "mintty: in main pid = %d\n", getpid());
143 | fprintf(stderr, "mintty: pty name = %s\n", pty_name);
144 | fprintf(stderr, "mintty: child pid = %d\n", pid);
145 |
146 | // wait a bit for child to login.
147 | //sleep(1);
148 |
149 | /*
150 | if (cfg.utmp) {
151 | char *dev = ptsname(pty_fd);
152 | if (dev) {
153 | struct utmp ut;
154 | memset(&ut, 0, sizeof ut);
155 |
156 | if (!strncmp(dev, "/dev/", 5))
157 | dev += 5;
158 | strlcpy(ut.ut_line, dev, sizeof ut.ut_line);
159 |
160 | if (dev[1] == 't' && dev[2] == 'y')
161 | dev += 3;
162 | else if (!strncmp(dev, "pts/", 4))
163 | dev += 4;
164 | strncpy(ut.ut_id, dev, sizeof ut.ut_id);
165 |
166 | ut.ut_type = USER_PROCESS;
167 | ut.ut_pid = pid;
168 | ut.ut_time = time(0);
169 | strlcpy(ut.ut_user, getlogin() ?: "?", sizeof ut.ut_user);
170 | gethostname(ut.ut_host, sizeof ut.ut_host);
171 | login(&ut);
172 | }
173 | }
174 | */
175 | }
176 |
177 | // Open log file if any
178 | if (*cfg.log) {
179 | if (!strcmp(cfg.log, "-"))
180 | log_fd = fileno(stdout);
181 | else {
182 | log_fd = open(cfg.log, O_WRONLY | O_CREAT | O_TRUNC, 0600);
183 | if (log_fd < 0)
184 | error("open log file");
185 | }
186 | }
187 | }
188 |
189 | void
190 | child_poll(void)
191 | {
192 | struct pollfd pfd = {pty_fd, POLLIN | POLLRDBAND, 0};
193 |
194 | int ret = poll(&pfd, 1, 0);
195 | if (ret < 0) {
196 | fprintf(stderr, "mintty: poll error = %s\n", strerror(errno));
197 | return;
198 | }
199 |
200 | if (pfd.revents & POLLRDBAND || pfd.revents & POLLIN) {
201 |
202 | if (term.paste_buffer)
203 | term_send_paste();
204 |
205 | static char buf[4096];
206 | int len = read(pty_fd, buf, sizeof buf);
207 |
208 | if (len == -1) {
209 | fprintf(stderr, "mintty: after read, pty_ft = %d, error = %s\n", pty_fd, strerror(errno));
210 | } else if (len > 0) {
211 | term_write(buf, len);
212 | if (log_fd >= 0)
213 | write(log_fd, buf, len);
214 | }
215 | }
216 | }
217 |
218 | void
219 | child_kill(bool point_blank)
220 | {
221 | if (!pid ||
222 | kill(-pid, point_blank ? SIGKILL : SIGHUP) < 0 ||
223 | point_blank)
224 | exit(0);
225 | killed = true;
226 | }
227 |
228 | bool
229 | child_is_alive(void)
230 | {
231 | return pid;
232 | }
233 |
234 | bool
235 | child_is_parent(void)
236 | {
237 | if (!pid)
238 | return false;
239 | DIR *d = opendir("/proc");
240 | if (!d)
241 | return false;
242 | bool res = false;
243 | struct dirent *e;
244 | char fn[18] = "/proc/";
245 | while ((e = readdir(d))) {
246 | char *pn = e->d_name;
247 | if (isdigit((uchar)*pn) && strlen(pn) <= 6) {
248 | snprintf(fn + 6, 12, "%s/ppid", pn);
249 | FILE *f = fopen(fn, "r");
250 | if (!f)
251 | continue;
252 | pid_t ppid = 0;
253 | fscanf(f, "%u", &ppid);
254 | fclose(f);
255 | if (ppid == pid) {
256 | res = true;
257 | break;
258 | }
259 | }
260 | }
261 | closedir(d);
262 | return res;
263 | }
264 |
265 | void
266 | child_write(const char *buf, uint len)
267 | {
268 | if (pty_fd >= 0)
269 | write(pty_fd, buf, len);
270 | }
271 |
272 | void
273 | child_printf(const char *fmt, ...)
274 | {
275 | if (pty_fd >= 0) {
276 | va_list va;
277 | va_start(va, fmt);
278 | char *s;
279 | int len = vasprintf(&s, fmt, va);
280 | va_end(va);
281 | if (len >= 0)
282 | write(pty_fd, s, len);
283 | free(s);
284 | }
285 | }
286 |
287 | void
288 | child_send(const char *buf, uint len)
289 | {
290 |
291 | /*
292 | fprintf(stderr, "mintty: child_send ");
293 | int i;
294 | for (i = 0; i < len; i++)
295 | fprintf(stderr, "%c", buf[i]);
296 | fprintf(stderr, "\n");
297 | */
298 |
299 | term_reset_screen();
300 | if (term.echoing)
301 | term_write(buf, len);
302 | child_write(buf, len);
303 | }
304 |
305 | void
306 | child_sendw(const wchar *ws, uint wlen)
307 | {
308 | char s[wlen * cs_cur_max];
309 | int len = cs_wcntombn(s, ws, sizeof s, wlen);
310 | if (len > 0)
311 | child_send(s, len);
312 | }
313 |
314 | void
315 | child_resize(struct winsize *winp)
316 | {
317 | if (pty_fd >= 0)
318 | ioctl(pty_fd, TIOCSWINSZ, winp);
319 | }
320 |
321 | mintty_wstring
322 | child_conv_path(mintty_wstring wpath)
323 | {
324 | #if 0
325 | int wlen = wcslen(wpath);
326 | int len = wlen * cs_cur_max;
327 | char path[len];
328 | len = cs_wcntombn(path, wpath, len, wlen);
329 | path[len] = 0;
330 |
331 | char *exp_path; // expanded path
332 | if (*path == '~') {
333 | // Tilde expansion
334 | char *name = path + 1;
335 | char *rest = strchr(path, '/');
336 | if (rest)
337 | *rest++ = 0;
338 | else
339 | rest = "";
340 | char *base;
341 | if (!*name)
342 | base = home;
343 | else {
344 | #if CYGWIN_VERSION_DLL_MAJOR >= 1005
345 | // Find named user's home directory
346 | struct passwd *pw = getpwnam(name);
347 | base = (pw ? pw->pw_dir : 0) ?: "";
348 | #else
349 | // Pre-1.5 Cygwin simply copies HOME into pw_dir, which is no use here.
350 | base = "";
351 | #endif
352 | }
353 | exp_path = asform("%s/%s", base, rest);
354 | }
355 | else if (*path != '/') {
356 | #if CYGWIN_VERSION_DLL_MAJOR >= 1005
357 | // Handle relative paths. Finding the foreground process working directory
358 | // requires the /proc filesystem, which isn't available before Cygwin 1.5.
359 |
360 | // Find pty's foreground process, if any. Fall back to child process.
361 | int fg_pid = (pty_fd >= 0) ? tcgetpgrp(pty_fd) : 0;
362 | if (fg_pid <= 0)
363 | fg_pid = pid;
364 |
365 | char *cwd = 0;
366 | if (fg_pid > 0) {
367 | char proc_cwd[32];
368 | sprintf(proc_cwd, "/proc/%u/cwd", fg_pid);
369 | cwd = realpath(proc_cwd, 0);
370 | }
371 |
372 | exp_path = asform("%s/%s", cwd ?: home, path);
373 | free(cwd);
374 | #else
375 | // If we're lucky, the path is relative to the home directory.
376 | exp_path = asform("%s/%s", home, path);
377 | #endif
378 | }
379 | else
380 | exp_path = path;
381 |
382 | #if CYGWIN_VERSION_DLL_MAJOR >= 1007
383 | #if CYGWIN_VERSION_API_MINOR >= 222
384 | // CW_INT_SETLOCALE was introduced in API 0.222
385 | cygwin_internal(CW_INT_SETLOCALE);
386 | #endif
387 | wchar *win_wpath = cygwin_create_path(CCP_POSIX_TO_WIN_W, exp_path);
388 |
389 | // Drop long path prefix if possible,
390 | // because some programs have trouble with them.
391 | if (win_wpath && wcslen(win_wpath) < MAX_PATH) {
392 | wchar *old_win_wpath = win_wpath;
393 | if (wcsncmp(win_wpath, L"\\\\?\\UNC\\", 8) == 0) {
394 | win_wpath = wcsdup(win_wpath + 6);
395 | win_wpath[0] = '\\'; // Replace "\\?\UNC\" prefix with "\\"
396 | free(old_win_wpath);
397 | }
398 | else if (wcsncmp(win_wpath, L"\\\\?\\", 4) == 0) {
399 | win_wpath = wcsdup(win_wpath + 4); // Drop "\\?\" prefix
400 | free(old_win_wpath);
401 | }
402 | }
403 | #else
404 | char win_path[MAX_PATH];
405 | cygwin_conv_to_win32_path(exp_path, win_path);
406 | wchar *win_wpath = newn(wchar, MAX_PATH);
407 | MultiByteToWideChar(0, 0, win_path, -1, win_wpath, MAX_PATH);
408 | #endif
409 |
410 | if (exp_path != path)
411 | free(exp_path);
412 |
413 | return win_wpath;
414 | #endif
415 | return 0;
416 | }
417 |
418 | void
419 | child_fork(char *argv[])
420 | {
421 | if (fork() == 0) {
422 | if (pty_fd >= 0)
423 | close(pty_fd);
424 | if (log_fd >= 0)
425 | close(log_fd);
426 | //close(win_fd);
427 |
428 | #if CYGWIN_VERSION_DLL_MAJOR >= 1005
429 | execv("/proc/self/exe", argv);
430 | #else
431 | // /proc/self/exe isn't available before Cygwin 1.5, so use argv[0] instead.
432 | // Strip enclosing quotes if present.
433 | char *path = argv[0];
434 | int len = strlen(path);
435 | if (path[0] == '"' && path[len - 1] == '"') {
436 | path = strdup(path + 1);
437 | path[len - 2] = 0;
438 | }
439 | execvp(path, argv);
440 | #endif
441 | exit(255);
442 | }
443 | }
444 |
--------------------------------------------------------------------------------
/mintty/child.h:
--------------------------------------------------------------------------------
1 | #ifndef CHILD_H
2 | #define CHILD_H
3 |
4 | #include
5 | #include "std.h"
6 |
7 | #ifdef LINUX
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #endif
14 |
15 | extern char *home, *cmd;
16 |
17 | void child_create(const char *argv[], struct winsize *winp);
18 | void child_poll(void);
19 | void child_kill(bool point_blank);
20 | void child_write(const char *, uint len);
21 | void child_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
22 | void child_send(const char *, uint len);
23 | void child_sendw(const wchar *, uint len);
24 | void child_resize(struct winsize *winp);
25 | bool child_is_alive(void);
26 | bool child_is_parent(void);
27 | mintty_wstring child_conv_path(mintty_wstring);
28 | void child_fork(char *argv[]);
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/mintty/config.h:
--------------------------------------------------------------------------------
1 | #ifndef CONFIG_H
2 | #define CONFIG_H
3 |
4 | #include "std.h"
5 |
6 | // Enums for various options.
7 |
8 | typedef enum { MDK_SHIFT = 1, MDK_ALT = 2, MDK_CTRL = 4 } mod_keys;
9 | enum { HOLD_NEVER, HOLD_START, HOLD_ERROR, HOLD_ALWAYS };
10 | enum { CUR_BLOCK, CUR_UNDERSCORE, CUR_LINE };
11 | enum { FS_DEFAULT, FS_PARTIAL, FS_NONE, FS_FULL };
12 | enum { RC_MENU, RC_PASTE, RC_EXTEND };
13 | enum { TR_OFF = 0, TR_LOW = 16, TR_MEDIUM = 32, TR_HIGH = 48, TR_GLASS = -1 };
14 |
15 |
16 | // Colour values.
17 |
18 | typedef uint colour;
19 |
20 | enum { DEFAULT_COLOUR = UINT_MAX };
21 |
22 | static inline colour
23 | make_colour(uchar r, uchar g, uchar b) { return r | g << 8 | b << 16; }
24 |
25 | bool parse_colour(mintty_string, colour *);
26 |
27 | static inline uchar red(colour c) { return c; }
28 | static inline uchar green(colour c) { return c >> 8; }
29 | static inline uchar blue(colour c) { return c >> 16; }
30 |
31 | typedef struct {
32 | float x, y, z;
33 | } vector_3;
34 |
35 | typedef struct {
36 | float x, y;
37 | } vector_2;
38 |
39 | // Font properties.
40 |
41 | typedef struct {
42 | mintty_string name;
43 | int size;
44 | bool isbold;
45 | } font_spec;
46 |
47 |
48 | // Configuration data.
49 |
50 | typedef struct {
51 | // Looks
52 | colour fg_colour, bg_colour, cursor_colour;
53 | char transparency;
54 | bool opaque_when_focused;
55 | char cursor_type;
56 | bool cursor_blinks;
57 | // Text
58 | font_spec font;
59 | char font_smoothing;
60 | char bold_as_font; // 0 = false, 1 = true, -1 = undefined
61 | bool bold_as_colour;
62 | bool allow_blinking;
63 | mintty_string locale;
64 | mintty_string charset;
65 | // Keys
66 | bool backspace_sends_bs;
67 | bool ctrl_alt_is_altgr;
68 | bool clip_shortcuts;
69 | bool window_shortcuts;
70 | bool switch_shortcuts;
71 | bool zoom_shortcuts;
72 | bool alt_fn_shortcuts;
73 | bool ctrl_shift_shortcuts;
74 | bool swap_alt_and_meta_keys;
75 | // Mouse
76 | bool copy_on_select;
77 | bool copy_as_rtf;
78 | bool clicks_place_cursor;
79 | char right_click_action;
80 | bool clicks_target_app;
81 | char click_target_mod;
82 | // Window
83 | vector_3 win_pos, win_rot;
84 | vector_2 win_anchor;
85 | float win_width;
86 | bool win_fullscreen;
87 | int cols, rows;
88 | int scrollback_lines;
89 | char scrollbar;
90 | char scroll_mod;
91 | bool pgupdn_scroll;
92 | // Terminal
93 | mintty_string term;
94 | mintty_string answerback;
95 | bool bell_sound;
96 | bool bell_flash;
97 | bool bell_taskbar;
98 | mintty_string printer;
99 | bool confirm_exit;
100 | // Command line
101 | mintty_string klass;
102 | char hold;
103 | mintty_string icon;
104 | mintty_string log;
105 | mintty_string title;
106 | bool utmp;
107 | char window;
108 | int x, y;
109 | // "Hidden"
110 | int col_spacing, row_spacing;
111 | mintty_string word_chars;
112 | colour ime_cursor_colour;
113 | colour ansi_colours[16];
114 | // Legacy
115 | bool use_system_colours;
116 | } config;
117 |
118 | extern config cfg, new_cfg;
119 |
120 | void init_config(void);
121 | void load_config(mintty_string filename);
122 | void set_arg_option(mintty_string name, mintty_string val);
123 | void parse_arg_option(mintty_string);
124 | void remember_arg(mintty_string);
125 | void finish_config(void);
126 | void copy_config(config *dst, const config *src);
127 |
128 | #endif
129 |
--------------------------------------------------------------------------------
/mintty/minibidi.h:
--------------------------------------------------------------------------------
1 | #ifndef MINIBIDI_H
2 | #define MINIBIDI_H
3 |
4 | #include "std.h"
5 |
6 | typedef struct {
7 | wchar origwc, wc;
8 | ushort index;
9 | } bidi_char;
10 |
11 | int do_bidi(bidi_char * line, int count);
12 | int do_shape(bidi_char * line, bidi_char * to, int count);
13 | bool is_rtl(wchar c);
14 |
15 | #endif
16 |
--------------------------------------------------------------------------------
/mintty/std.c:
--------------------------------------------------------------------------------
1 | // std.c (part of mintty)
2 | // Copyright 2010-11 Andy Koppe
3 | // Licensed under the terms of the GNU General Public License v3 or later.
4 |
5 | #include "std.h"
6 |
7 | void
8 | strset(mintty_string *sp, mintty_string s)
9 | {
10 | if (*sp) {
11 | uint size = strlen(s) + 1;
12 | *sp = memcpy(mintty_renewn((char *)*sp, size), s, size);
13 | } else {
14 | *sp = malloc(1);
15 | *((char *)*sp) = '\0';
16 | }
17 | }
18 |
19 | #if 0 //CYGWIN_VERSION_API_MINOR < 70
20 |
21 | int
22 | vasprintf(char **buf, const char *fmt, va_list va)
23 | {
24 | va_list va2;
25 | va_copy(va2, va);
26 | int len = vsnprintf(0, 0, fmt, va2);
27 | va_end(va2);
28 | if (len > 0) {
29 | *buf = malloc(len + 1);
30 | if (*buf)
31 | vsnprintf(*buf, len + 1, fmt, va);
32 | }
33 | else
34 | *buf = 0;
35 | return len;
36 | }
37 |
38 | int
39 | asprintf(char **buf, const char *fmt, ...)
40 | {
41 | va_list va;
42 | va_start(va, fmt);
43 | int len = vasprintf(buf, fmt, va);
44 | va_end(va);
45 | return len;
46 | }
47 |
48 | #endif
49 |
50 | char *
51 | asform(const char *fmt, ...)
52 | {
53 | char *s = 0;
54 | va_list va;
55 | va_start(va, fmt);
56 | vasprintf(&s, fmt, va);
57 | va_end(va);
58 | return s;
59 | }
60 |
61 |
62 | #if 0 //CYGWIN_VERSION_API_MINOR < 74
63 | int iswalnum(wint_t wc) { return wc < 0x100 && isalnum(wc); }
64 | int iswalpha(wint_t wc) { return wc < 0x100 && isalpha(wc); }
65 | int iswspace(wint_t wc) { return wc < 0x100 && isspace(wc); }
66 | #endif
67 |
68 |
69 | #if 0 //CYGWIN_VERSION_API_MINOR < 91
70 |
71 | /* Copyright (C) 2002 by Red Hat, Incorporated. All rights reserved.
72 | *
73 | * Permission to use, copy, modify, and distribute this software
74 | * is freely granted, provided that this notice is preserved.
75 | */
76 |
77 | int
78 | argz_create(char *const argv[], char **argz, size_t *argz_len)
79 | {
80 | int argc = 0;
81 | int i = 0;
82 | int len = 0;
83 | char *iter;
84 |
85 | *argz_len = 0;
86 |
87 | if (*argv == NULL)
88 | {
89 | *argz = NULL;
90 | return 0;
91 | }
92 |
93 | while (argv[argc])
94 | {
95 | *argz_len += (strlen(argv[argc]) + 1);
96 | argc++;
97 | }
98 |
99 | /* There are argc strings to copy into argz. */
100 | if(!(*argz = (char *)malloc(*argz_len)))
101 | return ENOMEM;
102 |
103 | iter = *argz;
104 | for(i = 0; i < argc; i++)
105 | {
106 | len = strlen(argv[i]) + 1;
107 | memcpy(iter, argv[i], len);
108 | iter += len;
109 | }
110 | return 0;
111 | }
112 |
113 | void
114 | argz_stringify(char *argz, size_t argz_len, int sep)
115 | {
116 | size_t i;
117 |
118 | /* len includes trailing \0, which we don't want to replace. */
119 | if (argz_len > 1)
120 | for (i = 0; i < argz_len - 1; i++)
121 | {
122 | if (argz[i] == '\0')
123 | argz[i] = sep;
124 | }
125 | }
126 | #endif
127 |
128 | #if 0 //CYGWIN_VERSION_API_MINOR < 93
129 |
130 | /*-
131 | * Copyright (c) 1990, 1993
132 | * The Regents of the University of California. All rights reserved.
133 | *
134 | * Redistribution and use in source and binary forms, with or without
135 | * modification, are permitted provided that the following conditions
136 | * are met:
137 | * 1. Redistributions of source code must retain the above copyright
138 | * notice, this list of conditions and the following disclaimer.
139 | * 2. Redistributions in binary form must reproduce the above copyright
140 | * notice, this list of conditions and the following disclaimer in the
141 | * documentation and/or other materials provided with the distribution.
142 | *
143 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
144 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
145 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
146 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
147 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
148 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
149 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
150 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
151 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
152 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
153 | * SUCH DAMAGE.
154 | *
155 | * CV 2003-09-10: Cygwin specific changes applied. Code simplified just
156 | * for Cygwin alone.
157 | */
158 |
159 | #include
160 | #include
161 | #include
162 |
163 | #define TTY_NAME_MAX 32
164 |
165 | int
166 | login_tty(int fd)
167 | {
168 | char *fdname;
169 | int newfd;
170 |
171 | if (setsid () == -1)
172 | return -1;
173 | if ((fdname = ttyname (fd)))
174 | {
175 | if (fd != STDIN_FILENO)
176 | close (STDIN_FILENO);
177 | if (fd != STDOUT_FILENO)
178 | close (STDOUT_FILENO);
179 | if (fd != STDERR_FILENO)
180 | close (STDERR_FILENO);
181 | newfd = open (fdname, O_RDWR);
182 | close (newfd);
183 | }
184 | dup2 (fd, STDIN_FILENO);
185 | dup2 (fd, STDOUT_FILENO);
186 | dup2 (fd, STDERR_FILENO);
187 | if (fd > 2)
188 | close (fd);
189 | return 0;
190 | }
191 |
192 | int
193 | openpty(int *amaster, int *aslave, char *name,
194 | const struct termios *termp, const struct winsize *winp)
195 | {
196 | int master, slave;
197 | char pts[TTY_NAME_MAX];
198 |
199 | if ((master = open ("/dev/ptmx", O_RDWR | O_NOCTTY)) >= 0)
200 | {
201 | grantpt (master);
202 | unlockpt (master);
203 | strcpy (pts, ptsname (master));
204 | if ((slave = open (pts, O_RDWR | O_NOCTTY)) >= 0)
205 | {
206 | if (amaster)
207 | *amaster = master;
208 | if (aslave)
209 | *aslave = slave;
210 | if (name)
211 | strcpy (name, pts);
212 | if (termp)
213 | tcsetattr (slave, TCSAFLUSH, termp);
214 | if (winp)
215 | ioctl (master, TIOCSWINSZ, (char *) winp);
216 | return 0;
217 | }
218 | close (master);
219 | }
220 | errno = ENOENT;
221 | return -1;
222 | }
223 |
224 | int
225 | forkpty(int *amaster, char *name,
226 | const struct termios *termp, const struct winsize *winp)
227 | {
228 | int master, slave, pid;
229 |
230 | if (openpty (&master, &slave, name, termp, winp) == -1)
231 | return -1;
232 | switch (pid = fork ())
233 | {
234 | case -1:
235 | return -1;
236 | case 0:
237 | close (master);
238 | login_tty (slave);
239 | return 0;
240 | }
241 | if (amaster)
242 | *amaster = master;
243 | close (slave);
244 | return pid;
245 | }
246 |
247 | #endif
248 |
--------------------------------------------------------------------------------
/mintty/std.h:
--------------------------------------------------------------------------------
1 | #ifndef STD_H
2 | #define STD_H
3 |
4 | //#include
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | #if 0 //CYGWIN_VERSION_API_MINOR >= 91
19 | #include
20 | #else
21 | int argz_create (char *const argv[], char **argz, size_t *argz_len);
22 | void argz_stringify (char *argz, size_t argz_len, int sep);
23 | #endif
24 |
25 | #if 1 //CYGWIN_VERSION_API_MINOR >= 74
26 | #include
27 | #else
28 | int iswalnum(wint_t);
29 | int iswalpha(wint_t);
30 | int iswspace(wint_t);
31 | #endif
32 |
33 | #if 0 //CYGWIN_VERSION_API_MINOR < 53
34 | #define strlcpy(dst, src, len) snprintf(dst, len, "%s", src)
35 | #endif
36 |
37 | #if 0 //CYGWIN_VERSION_API_MINOR < 70
38 | int asprintf(char **, const char *, ...);
39 | int vasprintf(char **, const char *, va_list);
40 | #endif
41 |
42 | char *asform(const char *fmt, ...);
43 |
44 | #if 0
45 | #define WINVER 0x500 // Windows 2000
46 | #define _WIN32_WINNT WINVER
47 | #define _WIN32_IE WINVER
48 | #include
49 | #endif
50 |
51 | #ifdef DMALLOC
52 | #include
53 | #endif
54 |
55 | #define always_inline __attribute__((always_inline)) inline
56 | #define unused(arg) unused_##arg __attribute__((unused))
57 | #define no_return __attribute__((noreturn)) void
58 |
59 | typedef signed char schar;
60 | typedef unsigned char uchar;
61 | typedef unsigned short ushort;
62 | typedef unsigned int uint;
63 |
64 | typedef void (*void_fn)(void);
65 |
66 | typedef uint xchar; // UTF-32
67 | typedef wchar_t wchar; // UTF-16
68 |
69 | typedef const char *mintty_string;
70 | typedef const wchar *mintty_wstring;
71 |
72 | #define null ((void *) 0)
73 |
74 | #define __W(s) L##s
75 | #define _W(s) __W(s)
76 |
77 | #define lengthof(array) (sizeof(array) / sizeof(*(array)))
78 | #define endof(array) (&(array)[lengthof(array)])
79 |
80 | #define mintty_new(type) ((type *)malloc(sizeof(type)))
81 | #define mintty_newn(type, n) ((type *)calloc((n), sizeof(type)))
82 | #define mintty_renewn(p, n) ((typeof(p)) realloc((p), sizeof(*p) * (n)))
83 | static inline void mintty_delete(const void *p) { free((void *)p); }
84 |
85 | void strset(mintty_string *sp, mintty_string s);
86 |
87 | #define WHEN break; case
88 | #define OR : case
89 | #define OTHERWISE break; default
90 |
91 | #ifdef TRACE
92 | #define trace(xs...) \
93 | printf("%s:%u:%s:", __FILE__, __LINE__, __func__); \
94 | printf(" " xs); \
95 | putchar('\n')
96 | #else
97 | #define trace(f, xs...) {}
98 | #endif
99 |
100 | #define sgn(x) ({ typeof(x) x_ = (x); (x_ > 0) - (x_ < 0); })
101 | #define sqr(x) ({ typeof(x) x_ = (x); x_ * x_; })
102 |
103 | #endif
104 |
--------------------------------------------------------------------------------
/mintty/term.h:
--------------------------------------------------------------------------------
1 | #ifndef TERM_H
2 | #define TERM_H
3 |
4 | #include "minibidi.h"
5 | #include "config.h"
6 |
7 | // Colour numbers
8 |
9 | typedef enum {
10 | // ANSI colours
11 | BLACK_I = 0,
12 | RED_I = 1,
13 | GREEN_I = 2,
14 | YELLOW_I = 3,
15 | BLUE_I = 4,
16 | MAGENTA_I = 5,
17 | CYAN_I = 6,
18 | WHITE_I = 7,
19 |
20 | // Bold ANSI colours
21 | BOLD_BLACK_I = 8,
22 | BOLD_RED_I = 9,
23 | BOLD_GREEN_I = 10,
24 | BOLD_YELLOW_I = 11,
25 | BOLD_BLUE_I = 12,
26 | BOLD_MAGENTA_I = 13,
27 | BOLD_CYAN_I = 14,
28 | BOLD_WHITE_I = 15,
29 |
30 | // Colour numbers 16 through 231 are occupied by a 6x6x6 colour cube,
31 | // with R at most significant and B at least. (36*R + 6*G + B + 16)
32 |
33 | // Colour numbers 232 through 255 are occupied by a uniform series of
34 | // gray shades running between black and white but not including either
35 | // on grounds of redundancy.
36 |
37 | // Default foreground
38 | FG_COLOUR_I = 256,
39 | BOLD_FG_COLOUR_I = 257,
40 |
41 | // Default background
42 | BG_COLOUR_I = 258,
43 | BOLD_BG_COLOUR_I = 259,
44 |
45 | // Cursor colours
46 | CURSOR_TEXT_COLOUR_I = 260,
47 | CURSOR_COLOUR_I = 261,
48 | IME_CURSOR_COLOUR_I = 262,
49 |
50 | // Number of colours
51 | COLOUR_NUM = 263
52 |
53 | } colour_i;
54 |
55 |
56 | /*
57 | * UCSWIDE is a special value used in the terminal data to signify
58 | * the character cell containing the right-hand half of a CJK wide
59 | * character.
60 | */
61 | enum { UCSWIDE = 0 };
62 |
63 | /* Three attribute types:
64 | * The ATTRs (normal attributes) are stored with the characters in
65 | * the main display arrays
66 | *
67 | * The TATTRs (temporary attributes) are generated on the fly, they
68 | * can overlap with characters but not with normal attributes.
69 | *
70 | * The LATTRs (line attributes) are an entirely disjoint space of
71 | * flags.
72 | *
73 | * The DATTRs (display attributes) are internal to terminal.c (but
74 | * defined here because their values have to match the others
75 | * here); they reuse the TATTR_* space but are always masked off
76 | * before sending to the front end.
77 | *
78 | * ATTR_INVALID is an illegal colour combination.
79 | */
80 | enum {
81 | ATTR_FGSHIFT = 0,
82 | ATTR_BGSHIFT = 9,
83 | ATTR_FGMASK = 0x00001FFu,
84 | ATTR_BGMASK = 0x003FE00u,
85 | ATTR_INVALID = 0x003FFFFu,
86 | ATTR_BOLD = 0x0040000u,
87 | ATTR_DIM = 0x0080000u,
88 | ATTR_INVISIBLE = 0x0100000u,
89 | ATTR_UNDER = 0x0200000u,
90 | ATTR_REVERSE = 0x0400000u,
91 | ATTR_BLINK = 0x0800000u,
92 | ATTR_PROTECTED = 0x1000000u,
93 | ATTR_WIDE = 0x2000000u,
94 | ATTR_NARROW = 0x4000000u,
95 |
96 | TATTR_RIGHTCURS = 0x10000000u, /* cursor-on-RHS */
97 | TATTR_PASCURS = 0x20000000u, /* passive cursor (box) */
98 | TATTR_ACTCURS = 0x40000000u, /* active cursor (block) */
99 | TATTR_COMBINING = 0x80000000u, /* combining characters */
100 |
101 | DATTR_STARTRUN = 0x80000000u, /* start of redraw run */
102 | DATTR_MASK = 0xF0000000u,
103 |
104 | LATTR_NORM = 0x00000000u,
105 | LATTR_WIDE = 0x00000001u,
106 | LATTR_TOP = 0x00000002u,
107 | LATTR_BOT = 0x00000003u,
108 | LATTR_MODE = 0x00000003u,
109 | LATTR_WRAPPED = 0x00000010u, /* this line wraps to next */
110 | LATTR_WRAPPED2 = 0x00000020u, /* with WRAPPED: CJK wide character
111 | * wrapped to next line, so last
112 | * single-width cell is empty */
113 | };
114 |
115 | enum {
116 | ATTR_DEFFG = FG_COLOUR_I << ATTR_FGSHIFT,
117 | ATTR_DEFBG = BG_COLOUR_I << ATTR_BGSHIFT,
118 | ATTR_DEFAULT = ATTR_DEFFG | ATTR_DEFBG,
119 | };
120 |
121 |
122 | typedef struct {
123 | /*
124 | * The cc_next field is used to link multiple termchars
125 | * together into a list, so as to fit more than one character
126 | * into a character cell (Unicode combining characters).
127 | *
128 | * cc_next is a relative offset into the current array of
129 | * termchars. I.e. to advance to the next character in a list,
130 | * one does `tc += tc->next'.
131 | *
132 | * Zero means end of list.
133 | */
134 | short cc_next;
135 |
136 | /*
137 | * Any code in terminal.c which definitely needs to be changed
138 | * when extra fields are added here is labelled with a comment
139 | * saying FULL-TERMCHAR.
140 | */
141 | wchar chr;
142 | uint attr;
143 |
144 | } termchar;
145 |
146 | extern const termchar basic_erase_char;
147 |
148 | typedef struct {
149 | ushort attr;
150 | ushort cols; /* number of real columns on the line */
151 | ushort size; /* number of allocated termchars
152 | (cc-lists may make this > cols) */
153 | bool temporary; /* true if decompressed from scrollback */
154 | short cc_free; /* offset to first cc in free list */
155 | termchar *chars;
156 | } termline;
157 |
158 | typedef termline *termlines;
159 |
160 | typedef struct {
161 | int width;
162 | termchar *chars;
163 | int *forward, *backward; /* the permutations of line positions */
164 | } bidi_cache_entry;
165 |
166 | termline *newline(int cols, int bce);
167 | void freeline(termline *);
168 | void clearline(termline *);
169 | void resizeline(termline *, int);
170 |
171 | int sblines(void);
172 | termline *fetch_line(int y);
173 | void release_line(termline *);
174 |
175 | int termchars_equal(termchar *a, termchar *b);
176 | int termchars_equal_override(termchar *a, termchar *b, uint bchr, uint battr);
177 |
178 | void copy_termchar(termline *destline, int x, termchar *src);
179 | void move_termchar(termline *line, termchar *dest, termchar *src);
180 |
181 | void add_cc(termline *, int col, wchar chr);
182 | void clear_cc(termline *, int col);
183 |
184 | uchar *compressline(termline *);
185 | termline *decompressline(uchar *, int *bytes_used);
186 |
187 | termchar *term_bidi_line(termline *, int scr_y);
188 |
189 | /* Traditional terminal character sets */
190 | typedef enum {
191 | CSET_ASCII = 'B', /* Normal ASCII charset */
192 | CSET_GBCHR = 'A', /* UK variant */
193 | CSET_LINEDRW = '0', /* Line drawing charset */
194 | CSET_OEM = 'U' /* OEM Codepage 437 */
195 | } term_cset;
196 |
197 | typedef struct {
198 | int y, x;
199 | } pos;
200 |
201 | typedef enum {
202 | MBT_LEFT = 1, MBT_MIDDLE = 2, MBT_RIGHT = 3
203 | } mouse_button;
204 |
205 | typedef struct belltime {
206 | struct belltime *next;
207 | uint ticks;
208 | } belltime;
209 |
210 | typedef struct {
211 | short x, y;
212 | uint attr;
213 | bool origin;
214 | bool autowrap;
215 | bool wrapnext;
216 | bool utf;
217 | bool g1;
218 | term_cset csets[2];
219 | uchar oem_acs;
220 | } term_cursor;
221 |
222 | struct term {
223 | bool on_alt_screen; /* On alternate screen? */
224 | bool show_other_screen;
225 |
226 | termlines *lines, *other_lines;
227 | term_cursor curs, saved_cursors[2];
228 |
229 | uchar **scrollback; /* lines scrolled off top of screen */
230 | int disptop; /* distance scrolled back (0 or -ve) */
231 | int sblen; /* length of scrollback buffer */
232 | int sblines; /* number of lines of scrollback */
233 | int sbpos; /* index of next scrollback position to be filled */
234 | int tempsblines; /* number of lines of .scrollback that
235 | * can be retrieved onto the terminal
236 | * ("temporary scrollback") */
237 |
238 | termlines *displines; /* buffer of text on real screen */
239 |
240 | termchar erase_char;
241 |
242 | char *inbuf; /* terminal input buffer */
243 | uint inbuf_size, inbuf_pos;
244 |
245 | bool rvideo; /* global reverse video flag */
246 | bool cursor_on; /* cursor enabled flag */
247 | bool deccolm_allowed; /* DECCOLM sequence for 80/132 cols allowed? */
248 | bool reset_132; /* Flag ESC c resets to 80 cols */
249 | bool cblinker; /* When blinking is the cursor on ? */
250 | bool tblinker; /* When the blinking text is on */
251 | bool blink_is_real; /* Actually blink blinking text */
252 | bool echoing; /* Does terminal want local echo? */
253 | bool insert; /* Insert mode */
254 | int marg_top, marg_bot; /* scroll margins */
255 | bool printing, only_printing; /* Are we doing ANSI printing? */
256 | int print_state; /* state of print-end-sequence scan */
257 | char *printbuf; /* buffered data for printer */
258 | uint printbuf_size, printbuf_pos;
259 |
260 | int rows, cols;
261 | bool has_focus;
262 | bool in_vbell;
263 |
264 | bool vt220_keys;
265 | bool shortcut_override;
266 | bool backspace_sends_bs;
267 | bool escape_sends_fs;
268 | bool app_escape_key;
269 | bool app_cursor_keys;
270 | bool app_keypad;
271 | bool app_wheel;
272 | bool wheel_reporting;
273 | int modify_other_keys;
274 | bool newline_mode;
275 | bool report_focus;
276 | bool report_ambig_width;
277 | bool bracketed_paste;
278 | bool show_scrollbar;
279 |
280 | int cursor_type;
281 | int cursor_blinks;
282 | bool cursor_invalid;
283 |
284 | uchar esc_mod; // Modifier character in escape sequences
285 |
286 | uint csi_argc;
287 | uint csi_argv[32];
288 |
289 | int cmd_num; // OSC command number, or -1 for DCS
290 | char cmd_buf[2048]; // OSC or DCS string buffer and length
291 | uint cmd_len;
292 |
293 | uchar *tabs;
294 |
295 | enum {
296 | NORMAL, ESCAPE, CSI_ARGS,
297 | IGNORE_STRING, CMD_STRING, CMD_ESCAPE,
298 | OSC_START, OSC_NUM, OSC_PALETTE
299 | } state;
300 |
301 | // Mouse mode
302 | enum {
303 | MM_NONE,
304 | MM_X10, // just clicks
305 | MM_VT200, // click and release
306 | MM_BTN_EVENT, // click, release, and drag with button down
307 | MM_ANY_EVENT // click, release, and any movement
308 | } mouse_mode;
309 |
310 | // Mouse encoding
311 | enum {
312 | ME_X10, // CSI M followed by one byte each for event, X and Y
313 | ME_UTF8, // Same as X10, but with UTF-8 encoded X and Y (ugly!)
314 | ME_URXVT_CSI, // CSI event ; x ; y M
315 | ME_XTERM_CSI // CSI > event ; x ; y M/m
316 | } mouse_enc;
317 |
318 | enum {
319 | // The state can be zero, one of the mouse buttons or one of the cases here.
320 | MS_SEL_CHAR = -1, MS_SEL_WORD = -2, MS_SEL_LINE = -3,
321 | MS_COPYING = -4, MS_PASTING = -5, MS_OPENING = -6
322 | } mouse_state;
323 |
324 | bool sel_rect, selected;
325 | pos sel_start, sel_end, sel_anchor;
326 |
327 | /* Scroll steps during selection when cursor out of window. */
328 | int sel_scroll;
329 | pos sel_pos;
330 |
331 | wchar *paste_buffer;
332 | int paste_len, paste_pos;
333 |
334 | /* True when we've seen part of a multibyte input char */
335 | bool in_mb_char;
336 |
337 | /* Non-zero when we've seen the first half of a surrogate pair */
338 | wchar high_surrogate;
339 |
340 | /*
341 | * These are buffers used by the bidi and Arabic shaping code.
342 | */
343 | termchar *ltemp;
344 | int ltemp_size;
345 | bidi_char *wcFrom, *wcTo;
346 | int wcFromTo_size;
347 | bidi_cache_entry *pre_bidi_cache, *post_bidi_cache;
348 | int bidi_cache_size;
349 | };
350 |
351 | extern struct term term;
352 |
353 | void term_init(void);
354 | void term_resize(int, int);
355 | void term_scroll(int, int);
356 | void term_reset(void);
357 | void term_clear_scrollback(void);
358 | void term_mouse_click(mouse_button, mod_keys, pos, int count);
359 | void term_mouse_release(mouse_button, mod_keys, pos);
360 | void term_mouse_move(mod_keys, pos);
361 | void term_mouse_wheel(int delta, int lines_per_notch, mod_keys, pos);
362 | void term_select_all(void);
363 | void term_paint(void);
364 | void term_invalidate(int left, int top, int right, int bottom);
365 | void term_open(void);
366 | void term_copy(void);
367 | void term_paste(wchar *, uint len);
368 | void term_send_paste(void);
369 | void term_cancel_paste(void);
370 | void term_reconfig(void);
371 | void term_flip_screen(void);
372 | void term_reset_screen(void);
373 | void term_write(const char *, uint len);
374 | void term_flush(void);
375 | void term_set_focus(bool has_focus);
376 | int term_cursor_type(void);
377 | bool term_cursor_blinks(void);
378 | void term_hide_cursor(void);
379 |
380 | #endif
381 |
--------------------------------------------------------------------------------
/mintty/termclip.c:
--------------------------------------------------------------------------------
1 | // termclip.c (part of mintty)
2 | // Copyright 2008-10 Andy Koppe
3 | // Adapted from code from PuTTY-0.60 by Simon Tatham and team.
4 | // Licensed under the terms of the GNU General Public License v3 or later.
5 |
6 | #include "termpriv.h"
7 |
8 | #include "win.h"
9 | #include "child.h"
10 | #include "charset.h"
11 |
12 | /*
13 | * Helper routine for term_copy(): growing buffer.
14 | */
15 | typedef struct {
16 | int buflen; /* amount of allocated space in textbuf/attrbuf */
17 | int bufpos; /* amount of actual data */
18 | wchar *textbuf; /* buffer for copied text */
19 | wchar *textptr; /* = textbuf + bufpos (current insertion point) */
20 | uint *attrbuf; /* buffer for copied attributes */
21 | uint *attrptr; /* = attrbuf + bufpos */
22 | } clip_workbuf;
23 |
24 | static void
25 | clip_addchar(clip_workbuf * b, wchar chr, int attr)
26 | {
27 | if (b->bufpos >= b->buflen) {
28 | b->buflen += 128;
29 | b->textbuf = mintty_renewn(b->textbuf, b->buflen);
30 | b->textptr = b->textbuf + b->bufpos;
31 | b->attrbuf = mintty_renewn(b->attrbuf, b->buflen);
32 | b->attrptr = b->attrbuf + b->bufpos;
33 | }
34 | *b->textptr++ = chr;
35 | *b->attrptr++ = attr;
36 | b->bufpos++;
37 | }
38 |
39 | static void
40 | get_selection(clip_workbuf *buf)
41 | {
42 | pos start = term.sel_start, end = term.sel_end;
43 |
44 | int old_top_x;
45 | int attr;
46 |
47 | buf->buflen = 5120;
48 | buf->bufpos = 0;
49 | buf->textptr = buf->textbuf = mintty_newn(wchar, buf->buflen);
50 | buf->attrptr = buf->attrbuf = mintty_newn(uint, buf->buflen);
51 |
52 | old_top_x = start.x; /* needed for rect==1 */
53 |
54 | while (poslt(start, end)) {
55 | bool nl = false;
56 | termline *line = fetch_line(start.y);
57 | pos nlpos;
58 |
59 | /*
60 | * nlpos will point at the maximum position on this line we
61 | * should copy up to. So we start it at the end of the
62 | * line...
63 | */
64 | nlpos.y = start.y;
65 | nlpos.x = term.cols;
66 |
67 | /*
68 | * ... move it backwards if there's unused space at the end
69 | * of the line (and also set `nl' if this is the case,
70 | * because in normal selection mode this means we need a
71 | * newline at the end)...
72 | */
73 | if (!(line->attr & LATTR_WRAPPED)) {
74 | while (nlpos.x && line->chars[nlpos.x - 1].chr == ' ' &&
75 | !line->chars[nlpos.x - 1].cc_next && poslt(start, nlpos))
76 | decpos(nlpos);
77 | if (poslt(nlpos, end))
78 | nl = true;
79 | }
80 | else if (line->attr & LATTR_WRAPPED2) {
81 | /* Ignore the last char on the line in a WRAPPED2 line. */
82 | decpos(nlpos);
83 | }
84 |
85 | /*
86 | * ... and then clip it to the terminal x coordinate if
87 | * we're doing rectangular selection. (In this case we
88 | * still did the above, so that copying e.g. the right-hand
89 | * column from a table doesn't fill with spaces on the
90 | * right.)
91 | */
92 | if (term.sel_rect) {
93 | if (nlpos.x > end.x)
94 | nlpos.x = end.x;
95 | nl = (start.y < end.y);
96 | }
97 |
98 | while (poslt(start, end) && poslt(start, nlpos)) {
99 | wchar cbuf[16], *p;
100 | int x = start.x;
101 |
102 | if (line->chars[x].chr == UCSWIDE) {
103 | start.x++;
104 | continue;
105 | }
106 |
107 | while (1) {
108 | wchar c = line->chars[x].chr;
109 | attr = line->chars[x].attr;
110 | cbuf[0] = c;
111 | cbuf[1] = 0;
112 |
113 | for (p = cbuf; *p; p++)
114 | clip_addchar(buf, *p, attr);
115 |
116 | if (line->chars[x].cc_next)
117 | x += line->chars[x].cc_next;
118 | else
119 | break;
120 | }
121 | start.x++;
122 | }
123 | if (nl) {
124 | clip_addchar(buf, '\r', 0);
125 | clip_addchar(buf, '\n', 0);
126 | }
127 | start.y++;
128 | start.x = term.sel_rect ? old_top_x : 0;
129 |
130 | release_line(line);
131 | }
132 | clip_addchar(buf, 0, 0);
133 | }
134 |
135 | void
136 | term_copy(void)
137 | {
138 | if (!term.selected)
139 | return;
140 |
141 | clip_workbuf buf;
142 | get_selection(&buf);
143 |
144 | /* Finally, transfer all that to the clipboard. */
145 | win_copy(buf.textbuf, buf.attrbuf, buf.bufpos);
146 | free(buf.textbuf);
147 | free(buf.attrbuf);
148 | }
149 |
150 | void
151 | term_open(void)
152 | {
153 | if (!term.selected)
154 | return;
155 | clip_workbuf buf;
156 | get_selection(&buf);
157 | free(buf.attrbuf);
158 |
159 | // Don't bother opening if it's all whitespace.
160 | wchar *p = buf.textbuf;
161 | while (iswspace(*p))
162 | p++;
163 | if (*p)
164 | win_open(buf.textbuf); // textbuf is freed by win_open
165 | else
166 | free(buf.textbuf);
167 | }
168 |
169 | void
170 | term_paste(wchar *data, uint len)
171 | {
172 | term_cancel_paste();
173 |
174 | term.paste_buffer = mintty_newn(wchar, len);
175 | term.paste_len = term.paste_pos = 0;
176 |
177 | // Copy data to the paste buffer, converting both Windows-style \r\n and
178 | // Unix-style \n line endings to \r, because that's what the Enter key sends.
179 | uint i;
180 | for (i = 0; i < len; i++) {
181 | wchar wc = data[i];
182 | if (wc != '\n')
183 | term.paste_buffer[term.paste_len++] = wc;
184 | else if (i == 0 || data[i - 1] != '\r')
185 | term.paste_buffer[term.paste_len++] = '\r';
186 | }
187 |
188 | if (term.bracketed_paste)
189 | child_write("\e[200~", 6);
190 | term_send_paste();
191 | }
192 |
193 | void
194 | term_cancel_paste(void)
195 | {
196 | if (term.paste_buffer) {
197 | free(term.paste_buffer);
198 | term.paste_buffer = 0;
199 | if (term.bracketed_paste)
200 | child_write("\e[201~", 6);
201 | }
202 | }
203 |
204 | void
205 | term_send_paste(void)
206 | {
207 | int i = term.paste_pos;
208 | while (i < term.paste_len && term.paste_buffer[i++] != '\r');
209 | child_sendw(term.paste_buffer + term.paste_pos, i - term.paste_pos);
210 | if (i < term.paste_len)
211 | term.paste_pos = i;
212 | else
213 | term_cancel_paste();
214 | }
215 |
216 | void
217 | term_select_all(void)
218 | {
219 | term.sel_start = (pos){-sblines(), 0};
220 | term.sel_end = (pos){term_last_nonempty_line(), term.cols};
221 | term.selected = true;
222 | if (cfg.copy_on_select)
223 | term_copy();
224 | }
225 |
--------------------------------------------------------------------------------
/mintty/termpriv.h:
--------------------------------------------------------------------------------
1 | #ifndef TERMPRIV_H
2 | #define TERMPRIV_H
3 |
4 | /*
5 | * Internal terminal functions and structs.
6 | */
7 |
8 | #include "term.h"
9 |
10 | #define incpos(p) ((p).x == term.cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0))
11 | #define decpos(p) ((p).x == 0 ? ((p).x = term.cols, (p).y--, 1) : ((p).x--, 0))
12 |
13 | #define poslt(p1,p2) ((p1).y < (p2).y || ((p1).y == (p2).y && (p1).x < (p2).x))
14 | #define posle(p1,p2) ((p1).y < (p2).y || ((p1).y == (p2).y && (p1).x <= (p2).x))
15 | #define poseq(p1,p2) ((p1).y == (p2).y && (p1).x == (p2).x)
16 | #define posdiff(p1,p2) (((p1).y - (p2).y) * (term.cols + 1) + (p1).x - (p2).x)
17 |
18 | /* Product-order comparisons for rectangular block selection. */
19 | #define posPlt(p1,p2) ((p1).y <= (p2).y && (p1).x < (p2).x)
20 | #define posPle(p1,p2) ((p1).y <= (p2).y && (p1).x <= (p2).x)
21 |
22 | void term_print_finish(void);
23 |
24 | void term_schedule_tblink(void);
25 | void term_schedule_cblink(void);
26 | void term_schedule_vbell(int already_started, int startpoint);
27 |
28 | void term_switch_screen(bool to_alt, bool reset);
29 | void term_check_boundary(int x, int y);
30 | void term_do_scroll(int topline, int botline, int lines, bool sb);
31 | void term_erase(bool selective, bool line_only, bool from_begin, bool to_end);
32 | int term_last_nonempty_line(void);
33 |
34 | static inline bool
35 | term_selecting(void)
36 | { return term.mouse_state < 0 && term.mouse_state >= MS_SEL_LINE; }
37 |
38 | void term_update_cs(void);
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/mintty/win.cpp:
--------------------------------------------------------------------------------
1 | extern "C" {
2 | #include "win.h"
3 | #include "charset.h"
4 | }
5 |
6 | #include "context.h"
7 | #include "font.h"
8 | #include "text.h"
9 |
10 | enum {LDRAW_CHAR_NUM = 31, LDRAW_CHAR_TRIES = 4};
11 |
12 | // TODO: REMOVE nobody appears to use linedraw_chars
13 |
14 | // VT100 linedraw character mappings for current font.
15 | wchar win_linedraw_chars[LDRAW_CHAR_NUM];
16 |
17 | bool font_ambig_wide;
18 |
19 | static const wchar linedraw_chars[LDRAW_CHAR_NUM][LDRAW_CHAR_TRIES] = {
20 | {0x25C6, 0x2666, '*'}, // 0x60 '`' Diamond
21 | {0x2592, '#'}, // 0x61 'a' Checkerboard (error)
22 | {0x2409, 0x2192, 0x01AD, 't'}, // 0x62 'b' Horizontal tab
23 | {0x240C, 0x21A1, 0x0192, 'f'}, // 0x63 'c' Form feed
24 | {0x240D, 0x21B5, 0x027C, 'r'}, // 0x64 'd' Carriage return
25 | {0x240A, 0x21B4, 0x019E, 'n'}, // 0x65 'e' Linefeed
26 | {0x00B0, 'o'}, // 0x66 'f' Degree symbol
27 | {0x00B1, '~'}, // 0x67 'g' Plus/minus
28 | {0x2424, 0x21B4, 0x019E, 'n'}, // 0x68 'h' Newline
29 | {0x240B, 0x2193, 0x028B, 'v'}, // 0x69 'i' Vertical tab
30 | {0x2518, '+'}, // 0x6A 'j' Lower-right corner
31 | {0x2510, '+'}, // 0x6B 'k' Upper-right corner
32 | {0x250C, '+'}, // 0x6C 'l' Upper-left corner
33 | {0x2514, '+'}, // 0x6D 'm' Lower-left corner
34 | {0x253C, '+'}, // 0x6E 'n' Crossing lines
35 | {0x23BA, 0x00AF, '-'}, // 0x6F 'o' High horizontal line
36 | {0x23BB, 0x00AF, '-'}, // 0x70 'p' Medium-high horizontal line
37 | {0x2500, 0x2015, 0x2014, '-'}, // 0x71 'q' Middle horizontal line
38 | {0x23BC, '_'}, // 0x72 'r' Medium-low horizontal line
39 | {0x23BF, '_'}, // 0x73 's' Low horizontal line
40 | {0x251C, '+'}, // 0x74 't' Left "T"
41 | {0x2524, '+'}, // 0x75 'u' Right "T"
42 | {0x2534, '+'}, // 0x76 'v' Bottom "T"
43 | {0x252C, '+'}, // 0x77 'w' Top "T"
44 | {0x2502, '|'}, // 0x78 'x' Vertical bar
45 | {0x2264, '#'}, // 0x79 'y' Less than or equal to
46 | {0x2265, '#'}, // 0x7A 'z' Greater than or equal to
47 | {0x03C0, '#'}, // 0x7B '{' Pi
48 | {0x2260, '#'}, // 0x7C '|' Not equal to
49 | {0x00A3, 'L'}, // 0x7D '}' UK pound sign
50 | {0x00B7, '.'}, // 0x7E '~' Centered dot
51 | };
52 |
53 | struct WIN_Context {
54 | WIN_Context() : max_advance(0), line_height(0) {}
55 | std::shared_ptr font;
56 | std::vector> textVec;
57 | uint32_t max_advance;
58 | uint32_t line_height;
59 | };
60 | WIN_Context* s_context = nullptr;
61 |
62 | char s_temp[4096];
63 | uint32_t s_ansi_colors[COLOUR_NUM];
64 |
65 | static uint32_t MakeColor(uint8_t red, uint8_t green, uint8_t blue)
66 | {
67 | uint8_t alpha = 255;
68 | return alpha << 24 | blue << 16 | green << 8 | red;
69 | }
70 |
71 | int win_get_text_count(void)
72 | {
73 | return s_context->textVec.size();
74 | }
75 |
76 | void* win_get_text(int i)
77 | {
78 | if (i < 0 || i >= s_context->textVec.size())
79 | return nullptr;
80 | else
81 | return (void*)(s_context->textVec[i].get());
82 | }
83 |
84 | static void init_colors(void)
85 | {
86 | // from http://en.wikipedia.org/wiki/ANSI_escape_code for terminal.app
87 | s_ansi_colors[BLACK_I] = MakeColor(0, 0, 0);
88 | s_ansi_colors[RED_I] = MakeColor(194, 54, 33);
89 | s_ansi_colors[GREEN_I] = MakeColor(37, 188, 36);
90 | s_ansi_colors[YELLOW_I] = MakeColor(173, 173, 39);
91 | s_ansi_colors[BLUE_I] = MakeColor(73, 46, 225);
92 | s_ansi_colors[MAGENTA_I] = MakeColor(211, 56, 211);
93 | s_ansi_colors[CYAN_I] = MakeColor(51, 187, 200);
94 | s_ansi_colors[WHITE_I] = MakeColor(203, 204, 205);
95 |
96 | s_ansi_colors[BOLD_BLACK_I] = MakeColor(129, 131, 131);
97 | s_ansi_colors[BOLD_RED_I] = MakeColor(252,57,31);
98 | s_ansi_colors[BOLD_GREEN_I] = MakeColor(49, 231, 34);
99 | s_ansi_colors[BOLD_YELLOW_I] = MakeColor(234, 236, 35);
100 | s_ansi_colors[BOLD_BLUE_I] = MakeColor(88, 51, 255);
101 | s_ansi_colors[BOLD_MAGENTA_I] = MakeColor(249, 53, 248);
102 | s_ansi_colors[BOLD_CYAN_I] = MakeColor(20, 240, 240);
103 | s_ansi_colors[BOLD_WHITE_I] = MakeColor(233, 235, 235);
104 |
105 | int r, g, b;
106 | for (r = 0; r < 6; r++) {
107 | for (g = 0; g < 6; g++) {
108 | for (b = 0; b < 6; b++) {
109 | assert((36 * r + 6 * g + b + 16) < COLOUR_NUM);
110 | s_ansi_colors[36 * r + 6 * g + b + 16] = MakeColor(r * 42, g * 42, b * 42);
111 | }
112 | }
113 | }
114 |
115 | int i;
116 | for (i = 0; i < 23; i++) {
117 | uint8_t intensity = (uint8_t)((i + 1) * (255.0f / 24.0f));
118 | assert((232 + i) < COLOUR_NUM);
119 | s_ansi_colors[232 + i] = MakeColor(intensity, intensity, intensity);
120 | }
121 |
122 | s_ansi_colors[FG_COLOUR_I] = s_ansi_colors[WHITE_I];
123 | s_ansi_colors[BOLD_FG_COLOUR_I] = s_ansi_colors[BOLD_WHITE_I];
124 |
125 | s_ansi_colors[BG_COLOUR_I] = s_ansi_colors[BLACK_I];
126 | s_ansi_colors[BOLD_BG_COLOUR_I] = s_ansi_colors[BOLD_BLACK_I];
127 |
128 | s_ansi_colors[CURSOR_TEXT_COLOUR_I] = MakeColor(0, 0, 0);
129 | s_ansi_colors[CURSOR_COLOUR_I] = MakeColor(233, 235, 235);
130 | s_ansi_colors[IME_CURSOR_COLOUR_I] = MakeColor(255, 255, 255);
131 | }
132 |
133 | void win_init(void)
134 | {
135 | init_colors();
136 | gb::Context::Init(1024, 1, gb::TextureFormat_Alpha);
137 |
138 | s_context = new WIN_Context();
139 |
140 | s_context->font = std::make_shared(cfg.font.name, cfg.font.size, 4,
141 | gb::FontRenderOption_Normal,
142 | gb::FontHintOption_Default);
143 |
144 | s_context->max_advance = s_context->font->GetMaxAdvance();
145 | s_context->line_height = s_context->font->GetLineHeight();
146 | }
147 |
148 | static void win_clear_text()
149 | {
150 | s_context->textVec.clear();
151 | }
152 |
153 | void win_shutdown(void)
154 | {
155 | delete s_context;
156 | s_context = nullptr;
157 |
158 | gb::Context::Shutdown();
159 | }
160 |
161 | void win_reconfig(void)
162 | {
163 | fprintf(stderr, "mintty: win_reconfig\n");
164 | fprintf(stderr, " rows = %d, cols = %d\n", cfg.rows, cfg.cols);
165 | }
166 |
167 | /* void win_update(void); */
168 | void win_update()
169 | {
170 | ;
171 | }
172 |
173 | /* void win_schedule_update(void); */
174 | void win_schedule_update(void)
175 | {
176 | //fprintf(stderr, "mintty: win_schedule_update()\n");
177 | win_clear_text();
178 |
179 | // NOTE: because of the way rendering works with OpenGL we need to rebuild all the text
180 | // when anything changes, it would be difficult to re-use the text from a previous frame.
181 | term_invalidate(0, 0, cfg.rows, cfg.cols);
182 |
183 | term_paint();
184 | }
185 |
186 | // returns number of leading zeros
187 | // hacker's delight (first edition) page 78
188 | static int nlz(uint32_t x)
189 | {
190 | if (x == 0)
191 | return 32;
192 | int n = 1;
193 | if ((x >> 16) == 0) { n = n + 16; x = x << 16; }
194 | if ((x >> 24) == 0) { n = n + 8; x = x << 8; }
195 | if ((x >> 28) == 0) { n = n + 4; x = x << 4; }
196 | if ((x >> 30) == 0) { n = n + 2; x = x << 2; }
197 | n = n - (x >> 31);
198 | return n;
199 | }
200 |
201 | /* void win_text(int x, int y, wchar *text, int len, uint attr, int lattr); */
202 | void win_text(int x, int y, wchar *text, int len, uint attr, int lattr)
203 | {
204 | int fgi = (attr & ATTR_FGMASK) >> ATTR_FGSHIFT;
205 | int bgi = (attr & ATTR_BGMASK) >> ATTR_BGSHIFT;
206 |
207 | if (term.rvideo) {
208 | if (fgi >= 256)
209 | fgi ^= 2;
210 | if (bgi >= 256)
211 | bgi ^= 2;
212 | }
213 | if (attr & ATTR_BOLD && cfg.bold_as_colour) {
214 | if (fgi < 8)
215 | fgi |= 8;
216 | else if (fgi >= 256 && !cfg.bold_as_font)
217 | fgi |= 1;
218 | }
219 | if (attr & ATTR_BLINK) {
220 | if (bgi < 8)
221 | bgi |= 8;
222 | else if (bgi >= 256)
223 | bgi |= 1;
224 | }
225 |
226 | bool is_cursor = (attr & (TATTR_ACTCURS | TATTR_PASCURS)) != 0;
227 |
228 | uint32_t fg_color = s_ansi_colors[fgi];
229 | uint32_t bg_color = s_ansi_colors[bgi];
230 |
231 | if (attr & ATTR_DIM) {
232 | fg_color = (fg_color & 0xFEFEFEFE) >> 1; // Halve the brightness.
233 | if (!cfg.bold_as_colour || fgi >= 256)
234 | fg_color += (bg_color & 0xFEFEFEFE) >> 1; // Blend with background.
235 | }
236 | if (attr & ATTR_REVERSE) {
237 | uint32_t temp = fg_color; fg_color = bg_color; bg_color = temp;
238 | }
239 | if (attr & ATTR_INVISIBLE)
240 | fg_color = bg_color;
241 |
242 | if (is_cursor) {
243 | bg_color = s_ansi_colors[CURSOR_COLOUR_I];
244 | fg_color = s_ansi_colors[CURSOR_TEXT_COLOUR_I];
245 | }
246 |
247 | //fprintf(stderr, "mintty: win_text() x = %d, y = %d, text = %p, len = %d, attr = 0x%x, lattr = 0x%x, fgi = %d, bgi = %d\n", x, y, text, len, attr, lattr, fgi, bgi);
248 |
249 | // HACK: because GB_Text does not have a pen position.
250 | // we append the string with the appropriate amount of newlines and spaces.
251 | // use s_temp to build string
252 |
253 | assert(len < 4096); // temp string overflow
254 |
255 | // convert text from utf16 into utf8 for gb_text
256 | // TODO: what about bom?
257 | int i, temp_len = 0;
258 | for (i = 0; i < len; i++) {
259 |
260 | uint32_t cp;
261 | if (is_high_surrogate(text[i])) {
262 | cp = combine_surrogates(text[i], text[i+1]);
263 | i++; // skip low surrogate
264 | } else {
265 | assert(!is_low_surrogate(text[i]));
266 | cp = text[i];
267 | }
268 |
269 | // encode cp as utf8
270 | int n = nlz(cp);
271 | int num_bits_needed = (32 - n);
272 | if (num_bits_needed < 8) {
273 | s_temp[temp_len++] = text[i];
274 | } else if (num_bits_needed < 11) {
275 | // 110x_xxxx 10yy_yyyy
276 | uint8_t x_bits = (0x07c0UL & cp) >> 6;
277 | uint8_t y_bits = (0x003fUL & cp);
278 | s_temp[temp_len++] = 0xc0 | x_bits;
279 | s_temp[temp_len++] = 0x80 | y_bits;
280 | } else if (num_bits_needed < 16) {
281 | // 1110_xxxx 10yy_yyyy 10zz_zzzz
282 | uint8_t x_bits = (0xf000UL & cp) >> 12;
283 | uint8_t y_bits = (0x0fc0UL & cp) >> 6;
284 | uint8_t z_bits = (0x003fUL & cp);
285 | s_temp[temp_len++] = 0xe0 | x_bits;
286 | s_temp[temp_len++] = 0x80 | y_bits;
287 | s_temp[temp_len++] = 0x80 | z_bits;
288 | } else {
289 | // 1111_0xxx 10yy_yyyy 10zz_zzzz 10ww_wwww
290 | uint8_t x_bits = (0x1c0000UL & cp) >> 18;
291 | uint8_t y_bits = (0x03f000UL & cp) >> 12;
292 | uint8_t z_bits = (0x000fc0UL & cp) >> 6;
293 | uint8_t w_bits = (0x00003fUL & cp);
294 | s_temp[temp_len++] = 0xf0 | x_bits;
295 | s_temp[temp_len++] = 0xe0 | y_bits;
296 | s_temp[temp_len++] = 0x80 | z_bits;
297 | s_temp[temp_len++] = 0x80 | w_bits;
298 | }
299 | }
300 | s_temp[temp_len] = 0;
301 | assert(temp_len < 4096); // temp string overflow.
302 |
303 | //fprintf(stderr, " -> %s\n", s_temp);
304 |
305 | // dump utf8 string
306 | /*
307 | fprintf(stderr, "s_temp = ");
308 | for (i = 0; i < temp_len; i++) {
309 | fprintf(stderr, "0x%x ", (uint8_t)s_temp[i]);
310 | }
311 | fprintf(stderr, "\n");
312 | */
313 |
314 | // allocate a new text object!
315 | uint32_t pixel_x = x * s_context->max_advance;
316 | uint32_t pixel_y = y * s_context->line_height;
317 | gb::IntPoint origin(pixel_x, pixel_y);
318 | gb::IntPoint size(10000, 10000);
319 | WIN_TextUserData* data = (WIN_TextUserData*)malloc(sizeof(struct WIN_TextUserData));
320 | data->fg_color = fg_color;
321 | data->bg_color = bg_color;
322 | data->max_advance = s_context->max_advance;
323 | data->line_height = s_context->line_height;
324 | auto gb_text = std::make_shared(s_temp, s_context->font, data, origin, size,
325 | gb::TextHorizontalAlign_Left,
326 | gb::TextVerticalAlign_Top,
327 | gb::TextOptionFlags_DisableShaping);
328 | s_context->textVec.push_back(gb_text);
329 | }
330 |
331 | int win_get_max_advance(void)
332 | {
333 | return s_context->max_advance;
334 | }
335 |
336 | int win_get_line_height(void)
337 | {
338 | return s_context->line_height;
339 | }
340 |
341 | // original win_text code from mintty
342 | #if 0
343 | /*
344 | * Draw a line of text in the window, at given character
345 | * coordinates, in given attributes.
346 | *
347 | * We are allowed to fiddle with the contents of `text'.
348 | */
349 | void
350 | win_text(int x, int y, wchar *text, int len, uint attr, int lattr)
351 | {
352 | lattr &= LATTR_MODE;
353 | int char_width = font_width * (1 + (lattr != LATTR_NORM));
354 |
355 | /* Convert to window coordinates */
356 | x = x * char_width + PADDING;
357 | y = y * font_height + PADDING;
358 |
359 | if (attr & ATTR_WIDE)
360 | char_width *= 2;
361 |
362 | /* Only want the left half of double width lines */
363 | if (lattr != LATTR_NORM && x * 2 >= term.cols)
364 | return;
365 |
366 | uint nfont;
367 | switch (lattr) {
368 | when LATTR_NORM: nfont = 0;
369 | when LATTR_WIDE: nfont = FONT_WIDE;
370 | otherwise: nfont = FONT_WIDE + FONT_HIGH;
371 | }
372 | if (attr & ATTR_NARROW)
373 | nfont |= FONT_NARROW;
374 |
375 | if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
376 | nfont |= FONT_BOLD;
377 | if (und_mode == UND_FONT && (attr & ATTR_UNDER))
378 | nfont |= FONT_UNDERLINE;
379 | another_font(nfont);
380 |
381 | bool force_manual_underline = false;
382 | if (!fonts[nfont]) {
383 | if (nfont & FONT_UNDERLINE)
384 | force_manual_underline = true;
385 | // Don't force manual bold, it could be bad news.
386 | nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
387 | }
388 | another_font(nfont);
389 | if (!fonts[nfont])
390 | nfont = FONT_NORMAL;
391 |
392 | colour_i fgi = (attr & ATTR_FGMASK) >> ATTR_FGSHIFT;
393 | colour_i bgi = (attr & ATTR_BGMASK) >> ATTR_BGSHIFT;
394 |
395 | if (term.rvideo) {
396 | if (fgi >= 256)
397 | fgi ^= 2;
398 | if (bgi >= 256)
399 | bgi ^= 2;
400 | }
401 | if (attr & ATTR_BOLD && cfg.bold_as_colour) {
402 | if (fgi < 8)
403 | fgi |= 8;
404 | else if (fgi >= 256 && !cfg.bold_as_font)
405 | fgi |= 1;
406 | }
407 | if (attr & ATTR_BLINK) {
408 | if (bgi < 8)
409 | bgi |= 8;
410 | else if (bgi >= 256)
411 | bgi |= 1;
412 | }
413 |
414 | colour fg = colours[fgi];
415 | colour bg = colours[bgi];
416 |
417 | if (attr & ATTR_DIM) {
418 | fg = (fg & 0xFEFEFEFE) >> 1; // Halve the brightness.
419 | if (!cfg.bold_as_colour || fgi >= 256)
420 | fg += (bg & 0xFEFEFEFE) >> 1; // Blend with background.
421 | }
422 | if (attr & ATTR_REVERSE) {
423 | colour t = fg; fg = bg; bg = t;
424 | }
425 | if (attr & ATTR_INVISIBLE)
426 | fg = bg;
427 |
428 | bool has_cursor = attr & (TATTR_ACTCURS | TATTR_PASCURS);
429 | colour cursor_colour = 0;
430 |
431 | if (has_cursor) {
432 | colour wanted_cursor_colour =
433 | colours[ime_open ? IME_CURSOR_COLOUR_I : CURSOR_COLOUR_I];
434 |
435 | bool too_close = colour_dist(wanted_cursor_colour, bg) < 32768;
436 |
437 | cursor_colour =
438 | too_close ? colours[CURSOR_TEXT_COLOUR_I] : wanted_cursor_colour;
439 |
440 | if ((attr & TATTR_ACTCURS) && term_cursor_type() == CUR_BLOCK) {
441 | bg = cursor_colour;
442 | fg = too_close ? wanted_cursor_colour : colours[CURSOR_TEXT_COLOUR_I];
443 | }
444 | }
445 |
446 | SelectObject(dc, fonts[nfont]);
447 | SetTextColor(dc, fg);
448 | SetBkColor(dc, bg);
449 |
450 | /* Check whether the text has any right-to-left characters */
451 | bool has_rtl = false;
452 | for (int i = 0; i < len && !has_rtl; i++)
453 | has_rtl = is_rtl(text[i]);
454 |
455 | uint eto_options = ETO_CLIPPED;
456 | if (has_rtl) {
457 | /* We've already done right-to-left processing in the screen buffer,
458 | * so stop Windows from doing it again (and hence undoing our work).
459 | * Don't always use this path because GetCharacterPlacement doesn't
460 | * do Windows font linking.
461 | */
462 | char classes[len];
463 | memset(classes, GCPCLASS_NEUTRAL, len);
464 |
465 | GCP_RESULTSW gcpr = {
466 | .lStructSize = sizeof(GCP_RESULTSW),
467 | .lpClass = (void *)classes,
468 | .lpGlyphs = text,
469 | .nGlyphs = len
470 | };
471 |
472 | GetCharacterPlacementW(dc, text, len, 0, &gcpr,
473 | FLI_MASK | GCP_CLASSIN | GCP_DIACRITIC);
474 | len = gcpr.nGlyphs;
475 | eto_options |= ETO_GLYPH_INDEX;
476 | }
477 |
478 | bool combining = attr & TATTR_COMBINING;
479 | int width = char_width * (combining ? 1 : len);
480 | RECT box = {
481 | .left = x, .top = y,
482 | .right = min(x + width, font_width * term.cols + PADDING),
483 | .bottom = y + font_height
484 | };
485 |
486 | /* Array with offsets between neighbouring characters */
487 | int dxs[len];
488 | int dx = combining ? 0 : char_width;
489 | for (int i = 0; i < len; i++)
490 | dxs[i] = dx;
491 |
492 | int yt = y + cfg.row_spacing - font_height * (lattr == LATTR_BOT);
493 |
494 | /* Finally, draw the text */
495 | SetBkMode(dc, OPAQUE);
496 | ExtTextOutW(dc, x, yt, eto_options | ETO_OPAQUE, &box, text, len, dxs);
497 |
498 | /* Shadow bold */
499 | if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
500 | SetBkMode(dc, TRANSPARENT);
501 | ExtTextOutW(dc, x + 1, yt, eto_options, &box, text, len, dxs);
502 | }
503 |
504 | /* Manual underline */
505 | if (lattr != LATTR_TOP &&
506 | (force_manual_underline ||
507 | (und_mode == UND_LINE && (attr & ATTR_UNDER)))) {
508 | int dec = (lattr == LATTR_BOT) ? descent * 2 - font_height : descent;
509 | HPEN oldpen = SelectObject(dc, CreatePen(PS_SOLID, 0, fg));
510 | MoveToEx(dc, x, y + dec, null);
511 | LineTo(dc, x + len * char_width, y + dec);
512 | oldpen = SelectObject(dc, oldpen);
513 | DeleteObject(oldpen);
514 | }
515 |
516 | if (has_cursor) {
517 | HPEN oldpen = SelectObject(dc, CreatePen(PS_SOLID, 0, cursor_colour));
518 | switch(term_cursor_type()) {
519 | when CUR_BLOCK:
520 | if (attr & TATTR_PASCURS) {
521 | HBRUSH oldbrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
522 | Rectangle(dc, x, y, x + char_width, y + font_height);
523 | SelectObject(dc, oldbrush);
524 | }
525 | when CUR_LINE: {
526 | int caret_width = 1;
527 | SystemParametersInfo(SPI_GETCARETWIDTH, 0, &caret_width, 0);
528 | if (caret_width > char_width)
529 | caret_width = char_width;
530 | if (attr & TATTR_RIGHTCURS)
531 | x += char_width - caret_width;
532 | if (attr & TATTR_ACTCURS) {
533 | HBRUSH oldbrush = SelectObject(dc, CreateSolidBrush(cursor_colour));
534 | Rectangle(dc, x, y, x + caret_width, y + font_height);
535 | DeleteObject(SelectObject(dc, oldbrush));
536 | }
537 | else if (attr & TATTR_PASCURS) {
538 | for (int dy = 0; dy < font_height; dy += 2)
539 | Polyline(
540 | dc, (POINT[]){{x, y + dy}, {x + caret_width, y + dy}}, 2);
541 | }
542 | }
543 | when CUR_UNDERSCORE:
544 | y += min(descent, font_height - 2);
545 | if (attr & TATTR_ACTCURS)
546 | Rectangle(dc, x, y, x + char_width, y + 2);
547 | else if (attr & TATTR_PASCURS) {
548 | for (int dx = 0; dx < char_width; dx += 2) {
549 | SetPixel(dc, x + dx, y, cursor_colour);
550 | SetPixel(dc, x + dx, y + 1, cursor_colour);
551 | }
552 | }
553 | }
554 | DeleteObject(SelectObject(dc, oldpen));
555 | }
556 | }
557 |
558 | #endif // if 0
559 |
560 | /* void win_update_mouse(void); */
561 | void win_update_mouse(void)
562 | {
563 | // change cursor type from arrow to bar
564 | // based on term.mouse_mode etc.
565 | }
566 |
567 | /* void win_capture_mouse(void); */
568 | /* void win_bell(void); */
569 | void win_bell(void)
570 | {
571 |
572 | }
573 |
574 | /* void win_set_title(char *); */
575 | void win_set_title(char *title)
576 | {
577 | // change window title
578 | }
579 |
580 | /* void win_save_title(void); */
581 | void win_save_title(void)
582 | {
583 | // save title?
584 | }
585 |
586 | /* void win_restore_title(void); */
587 | void win_restore_title(void)
588 | {
589 | // restore saved title?
590 | }
591 |
592 |
593 | /* colour win_get_colour(colour_i); */
594 | colour win_get_colour(colour_i i)
595 | {
596 | return 0;
597 | }
598 |
599 | /* void win_set_colour(colour_i, colour); */
600 | void win_set_colour(colour_i i, colour c)
601 | {
602 | ;
603 | }
604 |
605 | /* void win_reset_colours(void); */
606 | void win_reset_colours(void)
607 | {
608 | ;
609 | }
610 |
611 | colour win_get_sys_colour(bool fg)
612 | {
613 | return 0;
614 | }
615 |
616 | /* void win_invalidate_all(void); */
617 | void win_invalidate_all(void)
618 | {
619 | ;
620 | }
621 |
622 | /* void win_set_pos(int x, int y); */
623 | void win_set_pos(int x, int y)
624 | {
625 | // move window.
626 | }
627 |
628 | /* void win_set_chars(int rows, int cols); */
629 | void win_set_chars(int rows, int cols)
630 | {
631 | // resize window?
632 | }
633 |
634 | /* void win_set_pixels(int height, int width); */
635 | void win_set_pixels(int height, int width)
636 | {
637 | // resize window in pixels rather in characters
638 | }
639 |
640 | /* void win_maximise(int max); */
641 | void win_maximise(int max)
642 | {
643 | // 2 is fullscreen? 0 is not
644 | }
645 |
646 | /* void win_set_zorder(bool top); */
647 | void win_set_zorder(bool top)
648 | {
649 | // move to top or back.
650 | }
651 |
652 | /* void win_set_iconic(bool); */
653 | void win_set_iconic(bool iconic)
654 | {
655 | // minimize or restore window
656 | }
657 |
658 | /* void win_update_scrollbar(void); */
659 | void win_update_scrollbar(void)
660 | {
661 | // display or hide scroll bar
662 | // int scrollbar = term.show_scrollbar ? cfg.scrollbar : 0;
663 | }
664 |
665 | /* bool win_is_iconic(void); */
666 | bool win_is_iconic(void)
667 | {
668 | return 0;
669 | }
670 |
671 | /* void win_get_pos(int *xp, int *yp); */
672 | void win_get_pos(int *xp, int *yp)
673 | {
674 | *xp = 0;
675 | *yp = 0;
676 | }
677 |
678 | /* void win_get_pixels(int *height_p, int *width_p); */
679 | void win_get_pixels(int *height_p, int *width_p)
680 | {
681 | *height_p = 640;
682 | *width_p = 480;
683 | }
684 |
685 | /* void win_get_screen_chars(int *rows_p, int *cols_p); */
686 | void win_get_screen_chars(int *rows_p, int *cols_p)
687 | {
688 | *rows_p = 24;
689 | *cols_p = 80;
690 | }
691 |
692 | /* void win_popup_menu(void); */
693 |
694 | /* void win_zoom_font(int); */
695 | void win_zoom_font(int size)
696 | {
697 | // what?
698 | }
699 |
700 | /* void win_set_font_size(int); */
701 | void win_set_font_size(int size)
702 | {
703 | ;
704 | }
705 |
706 | /* uint win_get_font_size(void); */
707 | uint win_get_font_size(void)
708 | {
709 | return 10;
710 | }
711 |
712 |
713 | /* void win_check_glyphs(wchar *wcs, uint num); */
714 | void win_check_glyphs(wchar *wcs, uint num)
715 | {
716 | // huh?
717 | }
718 |
719 |
720 | /* void win_open(wstring path); */
721 | void win_open(mintty_wstring path)
722 | {
723 | // no clue.
724 | }
725 |
726 | /* void win_copy(const wchar *data, uint *attrs, int len); */
727 | void win_copy(const wchar *data, uint *attrs, int len)
728 | {
729 | // copy into system clipboard.
730 | }
731 |
732 | /* void win_paste(void); */
733 |
734 | /* void win_set_timer(void_fn cb, uint ticks); */
735 | void win_set_timer(void_fn cb, uint ticks)
736 | {
737 | ;
738 | }
739 |
740 | /* void win_show_about(void); */
741 | void win_show_error(wchar * error)
742 | {
743 | fprintf(stderr, "win_show_error %p\n", error);
744 | }
745 |
746 | /* bool win_is_glass_available(void); */
747 |
748 | /* int get_tick_count(void); */
749 | int get_tick_count(void)
750 | {
751 | return 1;
752 | }
753 |
754 | /* int cursor_blink_ticks(void); */
755 | int cursor_blink_ticks(void)
756 | {
757 | return 1;
758 | }
759 |
760 |
761 | /* int win_char_width(xchar); */
762 | int win_char_width(xchar c)
763 | {
764 | return 0;
765 | }
766 |
767 | /* wchar win_combine_chars(wchar bc, wchar cc); */
768 | /* Try to combine a base and combining character into a precomposed one.
769 | * Returns 0 if unsuccessful.
770 | */
771 | wchar win_combine_chars(wchar bc, wchar cc)
772 | {
773 | return 0;
774 | }
775 |
776 |
--------------------------------------------------------------------------------
/mintty/win.h:
--------------------------------------------------------------------------------
1 | #ifndef WIN_H
2 | #define WIN_H
3 |
4 | #include "term.h"
5 | #include
6 |
7 | struct WIN_TextUserData
8 | {
9 | uint32_t fg_color;
10 | uint32_t bg_color;
11 | uint32_t max_advance;
12 | uint32_t line_height;
13 | };
14 |
15 | int win_get_text_count(void);
16 | void* win_get_text(int i);
17 |
18 | int win_get_max_advance(void);
19 | int win_get_line_height(void);
20 |
21 | void win_init(void);
22 | void win_shutdown(void);
23 |
24 | void win_reconfig(void);
25 |
26 | void win_update(void);
27 | void win_schedule_update(void);
28 |
29 | void win_text(int x, int y, wchar *text, int len, uint attr, int lattr);
30 | void win_update_mouse(void);
31 | void win_capture_mouse(void);
32 | void win_bell(void);
33 |
34 | void win_set_title(char *);
35 | void win_save_title(void);
36 | void win_restore_title(void);
37 |
38 | colour win_get_colour(colour_i);
39 | void win_set_colour(colour_i, colour);
40 | void win_reset_colours(void);
41 | colour win_get_sys_colour(bool fg);
42 |
43 | void win_invalidate_all(void);
44 |
45 | void win_set_pos(int x, int y);
46 | void win_set_chars(int rows, int cols);
47 | void win_set_pixels(int height, int width);
48 | void win_maximise(int max);
49 | void win_set_zorder(bool top);
50 | void win_set_iconic(bool);
51 | void win_update_scrollbar(void);
52 | bool win_is_iconic(void);
53 | void win_get_pos(int *xp, int *yp);
54 | void win_get_pixels(int *height_p, int *width_p);
55 | void win_get_screen_chars(int *rows_p, int *cols_p);
56 | void win_popup_menu(void);
57 |
58 | void win_zoom_font(int);
59 | void win_set_font_size(int);
60 | uint win_get_font_size(void);
61 |
62 | void win_check_glyphs(wchar *wcs, uint num);
63 |
64 | void win_open(mintty_wstring path);
65 | void win_copy(const wchar *data, uint *attrs, int len);
66 | void win_paste(void);
67 |
68 | void win_set_timer(void_fn cb, uint ticks);
69 |
70 | void win_show_about(void);
71 | void win_show_error(wchar *);
72 |
73 | bool win_is_glass_available(void);
74 |
75 | int get_tick_count(void);
76 | int cursor_blink_ticks(void);
77 |
78 | int win_char_width(xchar);
79 | wchar win_combine_chars(wchar bc, wchar cc);
80 | extern wchar win_linedraw_chars[31];
81 |
82 | #endif
83 |
--------------------------------------------------------------------------------
/opengl.h:
--------------------------------------------------------------------------------
1 | #ifndef OPENGL_H
2 | #define OPENGL_H
3 |
4 | #ifdef __APPLE__
5 | #include "TargetConditionals.h"
6 | #endif
7 |
8 | #if defined DARWIN
9 | #include
10 | #include
11 | #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
12 | #include
13 | #include
14 | #else
15 | #define GL_GLEXT_PROTOTYPES 1
16 | #include
17 | #include
18 | #include
19 | #endif
20 |
21 | #endif // #define OPENGL_H
22 |
--------------------------------------------------------------------------------
/package/mac_app/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleGetInfoString
6 | riftty
7 | CFBundleExecutable
8 | launch
9 | CFBundleIdentifier
10 | org.hyperlogic.www
11 | CFBundleName
12 | riftty
13 | CFBundleIconFile
14 | icon.icns
15 | CFBundleShortVersionString
16 | 0.1
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundlePackageType
20 | APPL
21 | IFMajorVersion
22 | 0
23 | IFMinorVersion
24 | 1
25 |
26 |
27 |
--------------------------------------------------------------------------------
/package/mac_app/Contents/MacOS/launch:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cd "${0%/*}"
3 | ./riftty
4 |
--------------------------------------------------------------------------------
/package/mac_app/Contents/Resources/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperlogic/riftty/a06a93f4c148f3c148e0b956b7ff4c35032d51d0/package/mac_app/Contents/Resources/icon.icns
--------------------------------------------------------------------------------
/pty.c:
--------------------------------------------------------------------------------
1 | #include "pty.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | int Pty_Make(struct Pty **pty_out)
10 | {
11 | struct Pty *pty = calloc(1, sizeof(struct Pty));
12 | if (!pty) {
13 | return 0;
14 | }
15 |
16 | // NOTE: shamelessly copied from iTerm2 PTYTask.m
17 | #define CTRLKEY(c) ((c)-'A'+1)
18 |
19 | int isUTF8 = 0;
20 | // UTF-8 input will be added on demand.
21 | pty->term.c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT | (isUTF8 ? IUTF8 : 0);
22 | pty->term.c_oflag = OPOST | ONLCR;
23 | pty->term.c_cflag = CREAD | CS8 | HUPCL;
24 | pty->term.c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL;
25 |
26 | pty->term.c_cc[VEOF] = CTRLKEY('D');
27 | pty->term.c_cc[VEOL] = -1;
28 | pty->term.c_cc[VEOL2] = -1;
29 | pty->term.c_cc[VERASE] = 0x7f; // DEL
30 | pty->term.c_cc[VWERASE] = CTRLKEY('W');
31 | pty->term.c_cc[VKILL] = CTRLKEY('U');
32 | pty->term.c_cc[VREPRINT] = CTRLKEY('R');
33 | pty->term.c_cc[VINTR] = CTRLKEY('C');
34 | pty->term.c_cc[VQUIT] = 0x1c; // Control+backslash
35 | pty->term.c_cc[VSUSP] = CTRLKEY('Z');
36 | #ifndef LINUX
37 | pty->term.c_cc[VDSUSP] = CTRLKEY('Y');
38 | #endif
39 | pty->term.c_cc[VSTART] = CTRLKEY('Q');
40 | pty->term.c_cc[VSTOP] = CTRLKEY('S');
41 | pty->term.c_cc[VLNEXT] = CTRLKEY('V');
42 | pty->term.c_cc[VDISCARD] = CTRLKEY('O');
43 | pty->term.c_cc[VMIN] = 1;
44 | pty->term.c_cc[VTIME] = 0;
45 | #ifndef LINUX
46 | pty->term.c_cc[VSTATUS] = CTRLKEY('T');
47 | #endif
48 |
49 | pty->term.c_ispeed = B38400;
50 | pty->term.c_ospeed = B38400;
51 |
52 | pty->win.ws_row = 25;
53 | pty->win.ws_col = 80;
54 | pty->win.ws_xpixel = 0;
55 | pty->win.ws_ypixel = 0;
56 |
57 | /* Save real stderr so we can fprintf to it instead of pty for errors. */
58 | int STDERR = dup(STDERR_FILENO);
59 | FILE *parent_stderr = fdopen(STDERR, "w");
60 | setvbuf(parent_stderr, NULL, _IONBF, 0);
61 |
62 | int pid = forkpty(&pty->master_fd, pty->name, &pty->term, &pty->win);
63 | if (pid == (pid_t)0) {
64 | /* child process */
65 | char* const argv[] = {"login", "-pfl", "ajt", NULL};
66 | execvp(*argv, argv);
67 |
68 | /* exec error */
69 | fprintf(parent_stderr, "## exec failed ##\n");
70 | fprintf(parent_stderr, "filename=%s error=%s\n", *argv, strerror(errno));
71 |
72 | sleep(10);
73 | exit(-1);
74 | } else if (pid < (pid_t)0) {
75 | fprintf(stderr, "forkpty() failed: %s\n", strerror(errno));
76 | } else if (pid > (pid_t)0) {
77 | /* parent process */
78 | pty->child_pid = pid;
79 | fprintf(stdout, "pty name = %s\n", pty->name);
80 | fprintf(stdout, "child pid = %d\n", pid);
81 | *pty_out = pty;
82 | return 1;
83 | }
84 | return 0;
85 | }
86 |
87 | int Pty_Destroy(struct Pty *pty)
88 | {
89 | // TODO:
90 | return 1;
91 | }
92 |
93 | int Pty_Send(struct Pty *pty, const char* buffer, size_t size)
94 | {
95 | write(pty->master_fd, buffer, size);
96 | return 1;
97 | }
98 |
99 | int Pty_Read(struct Pty *pty, char *buffer, size_t size)
100 | {
101 | struct pollfd pfd = {pty->master_fd, POLLRDBAND, 0};
102 |
103 | poll(&pfd, 1, 0);
104 | if (pfd.revents & POLLRDBAND) {
105 | int bytes_read = read(pty->master_fd, buffer, size - 1);
106 | // null terminate string.
107 | if (bytes_read >= 0)
108 | buffer[bytes_read] = 0;
109 | return bytes_read;
110 | } else {
111 | return 0;
112 | }
113 | }
114 |
115 |
--------------------------------------------------------------------------------
/pty.h:
--------------------------------------------------------------------------------
1 | #ifndef PTY_H
2 | #define PTY_H
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | #ifdef DARWIN
9 | #include
10 | #include
11 | #endif
12 |
13 | #ifdef LINUX
14 | #include
15 | #include
16 | #include
17 | #include
18 | #endif
19 |
20 | struct Pty
21 | {
22 | int master_fd;
23 | pid_t child_pid;
24 | char name[PATH_MAX];
25 | struct termios term;
26 | struct winsize win;
27 | };
28 |
29 | // returns 1 on success 0 on failure
30 | int Pty_Make(struct Pty **pty_out);
31 |
32 | int Pty_Destroy(struct Pty *pty);
33 |
34 | // returns 1 on succes 0 on failure
35 | int Pty_Send(struct Pty *pty, const char *buffer, size_t size);
36 |
37 | // returns number of bytes read into buffer.
38 | int Pty_Read(struct Pty *pty, char *buffer, size_t size);
39 |
40 | #ifdef __cplusplus
41 | }
42 | #endif
43 |
44 | #endif
45 |
--------------------------------------------------------------------------------
/render.cpp:
--------------------------------------------------------------------------------
1 | #include "render.h"
2 | #include "shader.h"
3 | #include "opengl.h"
4 | #include "win.h"
5 | #include "text.h"
6 | #include
7 |
8 | #include "FullbrightShader.h"
9 | #include "FullbrightVertColorShader.h"
10 | #include "FullbrightTexturedShader.h"
11 | #include "FullbrightTexturedTextShader.h"
12 | #include "FullbrightTexturedVertColorShader.h"
13 | #include "FullbrightTexturedVertColorTextShader.h"
14 | #include "PhongTexturedShader.h"
15 |
16 | #ifdef DEBUG
17 | // If there is a glError this outputs it along with a message to stderr.
18 | // otherwise there is no output.
19 | void GLErrorCheck(const char* message)
20 | {
21 | GLenum val = glGetError();
22 | switch (val)
23 | {
24 | case GL_INVALID_ENUM:
25 | fprintf(stderr, "GL_INVALID_ENUM : %s\n", message);
26 | break;
27 | case GL_INVALID_VALUE:
28 | fprintf(stderr, "GL_INVALID_VALUE : %s\n", message);
29 | break;
30 | case GL_INVALID_OPERATION:
31 | fprintf(stderr, "GL_INVALID_OPERATION : %s\n", message);
32 | break;
33 | #ifndef GL_ES_VERSION_2_0
34 | case GL_STACK_OVERFLOW:
35 | fprintf(stderr, "GL_STACK_OVERFLOW : %s\n", message);
36 | break;
37 | case GL_STACK_UNDERFLOW:
38 | fprintf(stderr, "GL_STACK_UNDERFLOW : %s\n", message);
39 | break;
40 | #endif
41 | case GL_OUT_OF_MEMORY:
42 | fprintf(stderr, "GL_OUT_OF_MEMORY : %s\n", message);
43 | break;
44 | case GL_NO_ERROR:
45 | break;
46 | }
47 | }
48 | #endif
49 |
50 | FullbrightShader* s_fullbrightShader = 0;
51 | FullbrightVertColorShader* s_fullbrightVertColorShader = 0;
52 | FullbrightTexturedShader* s_fullbrightTexturedShader = 0;
53 | FullbrightTexturedTextShader* s_fullbrightTexturedTextShader = 0;
54 | FullbrightTexturedVertColorShader* s_fullbrightTexturedVertColorShader = 0;
55 | FullbrightTexturedVertColorTextShader* s_fullbrightTexturedVertColorTextShader = 0;
56 | PhongTexturedShader* s_phongTexturedShader = 0;
57 | Shader* s_prevShader = 0;
58 | GLuint s_checker = 0;
59 |
60 | static GLint CreateCheckerTexture()
61 | {
62 | GLuint retVal = 0;
63 | glGenTextures(1, &retVal);
64 | glBindTexture(GL_TEXTURE_2D, retVal);
65 |
66 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
67 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
68 |
69 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
70 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
71 |
72 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
73 |
74 | const int W = 512;
75 | const uint8_t O = 100;
76 | const uint8_t X = 255;
77 | uint8_t* checker = new uint8_t[W * W];
78 | int i, j;
79 | for (i = 0; i < W; i++) {
80 | for (j = 0; j < W; j++) {
81 | if (i < W/2) {
82 | if (j < W/2)
83 | checker[i * W + j] = O;
84 | else
85 | checker[i * W + j] = X;
86 | } else {
87 | if (j < W/2)
88 | checker[i * W + j] = X;
89 | else
90 | checker[i * W + j] = O;
91 | }
92 | }
93 | }
94 |
95 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, W, W, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, checker);
96 | delete [] checker;
97 | return retVal;
98 | }
99 |
100 | // prints all available OpenGL extensions
101 | static void _DumpExtensions()
102 | {
103 | const GLubyte* extensions = glGetString(GL_EXTENSIONS);
104 | printf("extensions =\n");
105 |
106 | std::string str((const char *)extensions);
107 | size_t s = 0;
108 | size_t t = str.find_first_of(' ', s);
109 | while (t != std::string::npos)
110 | {
111 | printf(" %s\n", str.substr(s, t - s).c_str());
112 | s = t + 1;
113 | t = str.find_first_of(' ', s);
114 | }
115 | }
116 |
117 | void RenderInit(bool verbose)
118 | {
119 | // print out gl version info
120 | if (verbose)
121 | {
122 | const GLubyte* version = glGetString(GL_VERSION);
123 | printf("OpenGL\n");
124 | printf(" version = %s\n", version);
125 |
126 | const GLubyte* vendor = glGetString(GL_VENDOR);
127 | printf(" vendor = %s\n", vendor);
128 |
129 | const GLubyte* renderer = glGetString(GL_RENDERER);
130 | printf(" renderer = %s\n", renderer);
131 |
132 | const GLubyte* shadingLanguageVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);
133 | printf(" shader language version = %s\n", shadingLanguageVersion);
134 |
135 | int maxTextureUnits;
136 | glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
137 | printf(" max texture units = %d\n", maxTextureUnits);
138 |
139 | #ifndef GL_ES_VERSION_2_0
140 | int maxVertexUniforms, maxFragmentUniforms;
141 | glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &maxVertexUniforms);
142 | glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &maxFragmentUniforms);
143 | printf(" max vertex uniforms = %d\n", maxVertexUniforms);
144 | printf(" max fragment uniforms = %d\n", maxFragmentUniforms);
145 | #endif
146 | }
147 |
148 | if (verbose)
149 | _DumpExtensions();
150 |
151 | s_fullbrightShader = new FullbrightShader();
152 | if (!s_fullbrightShader->compileAndLink())
153 | exit(EXIT_FAILURE);
154 |
155 | s_fullbrightVertColorShader = new FullbrightVertColorShader();
156 | if (!s_fullbrightVertColorShader->compileAndLink())
157 | exit(EXIT_FAILURE);
158 |
159 | s_fullbrightTexturedShader = new FullbrightTexturedShader();
160 | if (!s_fullbrightTexturedShader->compileAndLink())
161 | exit(EXIT_FAILURE);
162 |
163 | s_fullbrightTexturedTextShader = new FullbrightTexturedTextShader();
164 | if (!s_fullbrightTexturedTextShader->compileAndLink())
165 | exit(EXIT_FAILURE);
166 |
167 | s_fullbrightTexturedVertColorShader = new FullbrightTexturedVertColorShader();
168 | if (!s_fullbrightTexturedVertColorShader->compileAndLink())
169 | exit(EXIT_FAILURE);
170 |
171 | s_fullbrightTexturedVertColorTextShader = new FullbrightTexturedVertColorTextShader();
172 | if (!s_fullbrightTexturedVertColorTextShader->compileAndLink())
173 | exit(EXIT_FAILURE);
174 |
175 | s_phongTexturedShader = new PhongTexturedShader();
176 | if (!s_phongTexturedShader->compileAndLink())
177 | exit(EXIT_FAILURE);
178 |
179 | s_checker = CreateCheckerTexture();
180 | }
181 |
182 | void RenderShutdown()
183 | {
184 | delete s_fullbrightShader;
185 | delete s_fullbrightVertColorShader;
186 | delete s_fullbrightTexturedShader;
187 | delete s_fullbrightTexturedTextShader;
188 | delete s_fullbrightTexturedVertColorShader;
189 | delete s_fullbrightTexturedVertColorTextShader;
190 | delete s_phongTexturedShader;
191 | }
192 |
193 | static Vector4f UintColorToVector4(uint32_t color)
194 | {
195 | return Vector4f((color & 0xff) / 255.0f,
196 | ((color >> 8) & 0xff) / 255.0f,
197 | ((color >> 16) & 0xff) / 255.0f,
198 | ((color >> 24) & 0xff) / 255.0f);
199 | }
200 |
201 | void RenderBegin()
202 | {
203 | s_prevShader = 0;
204 | }
205 |
206 | void RenderEnd()
207 | {
208 |
209 | }
210 |
211 | void RenderTextBegin(const Matrixf& projMatrix, const Matrixf& viewMatrix, const Matrixf& modelMatrix)
212 | {
213 | Matrixf fullMatrix = projMatrix * viewMatrix * modelMatrix;
214 | s_fullbrightShader->setMat(fullMatrix);
215 | s_fullbrightVertColorShader->setMat(fullMatrix);
216 | s_fullbrightTexturedShader->setMat(fullMatrix);
217 | s_fullbrightTexturedTextShader->setMat(fullMatrix);
218 | s_fullbrightTexturedVertColorShader->setMat(fullMatrix);
219 | s_fullbrightTexturedVertColorTextShader->setMat(fullMatrix);
220 | }
221 |
222 | void RenderText(const std::vector& quadVec)
223 | {
224 | static uint16_t indices[] = {0, 2, 1, 2, 3, 1};
225 |
226 | static std::vector s_attribVec;
227 | static std::vector s_indexVec;
228 |
229 | s_attribVec.clear();
230 | s_indexVec.clear();
231 |
232 | // disable depth writes
233 | glDepthMask(GL_FALSE);
234 |
235 | Vector2f minCorner(FLT_MAX, FLT_MAX);
236 | Vector2f maxCorner(-FLT_MAX, -FLT_MAX);
237 |
238 | // draw bg quads
239 | int i = 0;
240 | for (auto &quad : quadVec)
241 | {
242 | const WIN_TextUserData* data = (const WIN_TextUserData*)quad.userData;
243 |
244 | Vector4f bg_color = UintColorToVector4(data->bg_color);
245 | // TODO: but this in cfg
246 | bg_color.w = 0.85f;
247 |
248 | uint32_t y_offset = data->line_height / 3; // hack
249 | Vector2f origin = Vector2f(quad.pen.x, quad.pen.y + y_offset);
250 | Vector2f size = Vector2f(data->max_advance, -(float)data->line_height);
251 |
252 | // keep track of min and max corner.
253 | if (i == 0)
254 | minCorner = origin;
255 | else if (i == quadVec.size() - 1)
256 | maxCorner = origin + size;
257 |
258 | float attrib[28] = {
259 | origin.x, origin.y, 0,
260 | bg_color.x, bg_color.y, bg_color.z, bg_color.w,
261 | origin.x + size.x, origin.y, 0,
262 | bg_color.x, bg_color.y, bg_color.z, bg_color.w,
263 | origin.x, origin.y + size.y, 0,
264 | bg_color.x, bg_color.y, bg_color.z, bg_color.w,
265 | origin.x + size.x, origin.y + size.y, 0,
266 | bg_color.x, bg_color.y, bg_color.z, bg_color.w
267 | };
268 | for (int j = 0; j < 28; j++)
269 | s_attribVec.push_back(attrib[j]);
270 | for (int j = 0; j < 6; j++)
271 | s_indexVec.push_back(4 * i + indices[j]);
272 | i++;
273 | }
274 | s_fullbrightVertColorShader->apply(s_prevShader, reinterpret_cast(&s_attribVec[0]));
275 | s_prevShader = s_fullbrightVertColorShader;
276 | glDrawElements(GL_TRIANGLES, s_indexVec.size(), GL_UNSIGNED_SHORT, reinterpret_cast(&s_indexVec[0]));
277 |
278 | s_attribVec.clear();
279 | s_indexVec.clear();
280 |
281 | // hack: assuming the texture for the first quad is relevent for all of them!
282 | s_fullbrightTexturedVertColorTextShader->setTex(quadVec[0].glTexObj);
283 | s_fullbrightTexturedVertColorTextShader->setLodBias(-1.0f);
284 |
285 | // draw fg glyphs
286 | const float kDepthOffset = 0.0f;
287 | i = 0;
288 | for (auto &quad : quadVec) {
289 | const WIN_TextUserData* data = (const WIN_TextUserData*)quad.userData;
290 |
291 | if (quad.size.x > 0 && quad.size.y > 0)
292 | {
293 | const float inset = 2.0f;
294 | const float uvInset = inset / 1024.0f; // HACK 1024 is hardcoded size of font texture cache.
295 | Vector2f origin = Vector2f(quad.origin.x + inset, quad.origin.y + inset);
296 | Vector2f size = Vector2f(quad.size.x - 2*inset, quad.size.y - 2*inset);
297 | Vector2f uv_origin = Vector2f(quad.uvOrigin.x + uvInset, quad.uvOrigin.y + uvInset);
298 | Vector2f uv_size = Vector2f(quad.uvSize.x - 2*uvInset, quad.uvSize.y - 2*uvInset);
299 | Vector4f fg_color = UintColorToVector4(data->fg_color);
300 | float attrib[36] = {
301 | origin.x, origin.y, kDepthOffset, uv_origin.x, uv_origin.y,
302 | fg_color.x, fg_color.y, fg_color.z, fg_color.w,
303 | origin.x + size.x, origin.y, kDepthOffset, uv_origin.x + uv_size.x, uv_origin.y,
304 | fg_color.x, fg_color.y, fg_color.z, fg_color.w,
305 | origin.x, origin.y + size.y, kDepthOffset, uv_origin.x, uv_origin.y + uv_size.y,
306 | fg_color.x, fg_color.y, fg_color.z, fg_color.w,
307 | origin.x + size.x, origin.y + size.y, kDepthOffset, uv_origin.x + uv_size.x, uv_origin.y + uv_size.y,
308 | fg_color.x, fg_color.y, fg_color.z, fg_color.w
309 | };
310 |
311 | for (int j = 0; j < 36; j++)
312 | s_attribVec.push_back(attrib[j]);
313 | for (int j = 0; j < 6; j++)
314 | s_indexVec.push_back(4 * i + indices[j]);
315 | i++;
316 | }
317 | }
318 |
319 | s_fullbrightTexturedVertColorTextShader->apply(s_prevShader, reinterpret_cast(&s_attribVec[0]));
320 | s_prevShader = s_fullbrightTexturedVertColorTextShader;
321 | glDrawElements(GL_TRIANGLES, s_indexVec.size(), GL_UNSIGNED_SHORT, reinterpret_cast(&s_indexVec[0]));
322 |
323 | // enable depth writes
324 | glDepthMask(GL_TRUE);
325 |
326 | // disable color buffer writes
327 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
328 |
329 | if (quadVec.size())
330 | {
331 | float pos[12] = {
332 | minCorner.x, minCorner.y, 0,
333 | maxCorner.x, minCorner.y, 0,
334 | minCorner.x, maxCorner.y, 0,
335 | maxCorner.x, maxCorner.y, 0
336 | };
337 |
338 | s_fullbrightShader->apply(s_prevShader, pos);
339 | s_prevShader = s_fullbrightShader;
340 |
341 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
342 | }
343 |
344 | // re-enable color buffer writes.
345 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
346 |
347 | GL_ERROR_CHECK("TextRenderFunc");
348 | }
349 |
350 | void RenderTextEnd()
351 | {
352 |
353 | }
354 |
355 | void RenderFloor(const Matrixf& projMatrix, const Matrixf& viewMatrix, float height)
356 | {
357 | Matrixf worldMatrix = Matrixf::Trans(Vector3f(0, height, 0));
358 | Matrixf normalMatrix = worldMatrix;
359 | normalMatrix.SetTrans(Vector3f(0, 0, 0));
360 |
361 | const float kFeetToMeters = 0.3048;
362 |
363 | s_phongTexturedShader->setFullMat(projMatrix * viewMatrix * worldMatrix);
364 | s_phongTexturedShader->setWorldMat(worldMatrix);
365 | s_phongTexturedShader->setWorldNormalMat(normalMatrix);
366 | s_phongTexturedShader->setColor(Vector4f(1, 1, 1, 1));
367 | s_phongTexturedShader->setTex(s_checker);
368 |
369 | static bool init = false;
370 | if (!init) {
371 | std::vector lightWorldPos;
372 | lightWorldPos.push_back(Vector3f(0, 4 * kFeetToMeters, 0));
373 | lightWorldPos.push_back(Vector3f(10 * kFeetToMeters, -1, -1.0f));
374 | lightWorldPos.push_back(Vector3f(0, 4 * kFeetToMeters, 0));
375 | s_phongTexturedShader->setLightWorldPos(lightWorldPos);
376 |
377 | std::vector lightColor;
378 | lightColor.push_back(Vector3f(1, 1, 1));
379 | lightColor.push_back(Vector3f(1, 0, 0));
380 | lightColor.push_back(Vector3f(0, 0, 1));
381 | s_phongTexturedShader->setLightColor(lightColor);
382 |
383 | std::vector lightStrength;
384 | lightStrength.push_back(10);
385 | lightStrength.push_back(5);
386 | lightStrength.push_back(5);
387 | s_phongTexturedShader->setLightStrength(lightStrength);
388 |
389 | s_phongTexturedShader->setNumLights(3);
390 | init = true;
391 | }
392 |
393 | float kOffset = 1000.0f * kFeetToMeters;
394 | float kTexOffset = 100.0f;
395 | float attrib[32] = {
396 | 0 - kOffset, 0, 0 - kOffset, 0, 0, 0, 1, 0,
397 | 0 + kOffset, 0, 0 - kOffset, 0 + kTexOffset, 0, 0, 1, 0,
398 | 0 - kOffset, 0, 0 + kOffset, 0, 0 + kTexOffset, 0, 1, 0,
399 | 0 + kOffset, 0, 0 + kOffset, 0 + kTexOffset, 0 + kTexOffset, 0, 1, 0
400 | };
401 | s_phongTexturedShader->apply(s_prevShader, attrib);
402 |
403 | static uint16_t indices[] = {0, 2, 1, 2, 3, 1};
404 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
405 | }
406 |
407 | void RenderScreenAlignedQuad(GLuint texture, Vector2f viewportSize, Vector2f quadPos, Vector2f quadSize)
408 | {
409 | Matrixf projMatrix = Matrixf::Ortho(0, viewportSize.x, 0, viewportSize.y, -1, 1);
410 |
411 | s_fullbrightTexturedShader->setMat(projMatrix);
412 | s_fullbrightTexturedShader->setColor(Vector4f(1, 1, 1, 1));
413 | s_fullbrightTexturedShader->setTex(texture);
414 |
415 | float attrib[20] = {
416 | quadPos.x, quadPos.y, 0, 0, 0,
417 | quadPos.x + quadSize.x, quadPos.y, 0, 1, 0,
418 | quadPos.x, quadPos.y + quadSize.y, 0, 0, 1,
419 | quadPos.x + quadSize.x, quadPos.y + quadSize.y, 0, 1, 1
420 | };
421 |
422 | s_fullbrightTexturedShader->apply(s_prevShader, attrib);
423 | s_prevShader = s_fullbrightTexturedShader;
424 |
425 | static uint16_t indices[] = {0, 2, 1, 2, 3, 1};
426 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
427 | }
428 |
--------------------------------------------------------------------------------
/render.h:
--------------------------------------------------------------------------------
1 | #ifndef RENDER_H
2 | #define RENDER_H
3 |
4 | #include
5 | #include "abaci.h"
6 |
7 | namespace gb {
8 | struct Quad;
9 | }
10 |
11 | void RenderInit(bool verbose = false);
12 |
13 | void RenderBegin();
14 | void RenderEnd();
15 |
16 | void RenderTextBegin(const Matrixf& projMatrix, const Matrixf& viewMatrix, const Matrixf& modelMatrix);
17 | void RenderText(const std::vector& quadVec);
18 | void RenderTextEnd();
19 |
20 | void RenderFloor(const Matrixf& projMatrix, const Matrixf& viewMatrix, float height);
21 | void RenderScreenAlignedQuad(unsigned int texture, Vector2f viewportSize, Vector2f quadPos, Vector2f quadSize);
22 |
23 | #ifdef DEBUG
24 | #define GL_ERROR_CHECK(x) GLErrorCheck(x)
25 | void GLErrorCheck(const char* message);
26 | #else
27 | #define GL_ERROR_CHECK(x)
28 | #endif
29 |
30 | #endif // #define RENDER_H
31 |
--------------------------------------------------------------------------------
/restart.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/ruby
2 | require 'date'
3 |
4 | # return 1 if there is only one riftty process.
5 | # return 0 if there is already a riftty process, and restart it.
6 |
7 | def get_procs
8 | procs = []
9 | `ps -caopid,comm,lstart`.each_line do |line|
10 | match = /\s*(\S*)\s*(\S*)\s*(.*)/.match(line)
11 | pid, comm, lstart = match[1], match[2], match[3]
12 | if pid != 'PID'
13 | time = DateTime.strptime(lstart, "%c")
14 | procs.push({ time: time, pid: pid, comm: comm })
15 | end
16 | end
17 | procs
18 | end
19 |
20 | procs = get_procs.select {|m| m[:comm] == 'riftty'}.sort_by {|m| m[:time]}
21 |
22 | # debug
23 | procs.each {|m| puts m.inspect}
24 |
25 | if procs.size == 2
26 | # send sigusr1 to old process
27 | `kill -30 #{procs[0][:pid]}`
28 | exit 0 # success
29 | else
30 | exit 1 # failure
31 | end
32 |
33 |
--------------------------------------------------------------------------------
/riftty.cpp:
--------------------------------------------------------------------------------
1 |
2 | #if (defined _WIN32) || (defined _WIN64)
3 | #include "SDL.h"
4 | #include "SDL_opengl.h"
5 | #else
6 | #include "SDL.h"
7 | #endif
8 |
9 | #include
10 | #include
11 | #include "keyboard.h"
12 | #include "opengl.h"
13 | #include "render.h"
14 | #include "joystick.h"
15 |
16 | #include "text.h"
17 | #include "context.h"
18 |
19 | #include "abaci.h"
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include
27 | #include
28 | #include
29 |
30 | #include "render.h"
31 | #include "shader.h"
32 |
33 | extern "C" {
34 | #include "config.h"
35 | #include "charset.h"
36 | #include "term.h"
37 | #include "child.h"
38 | #include "win.h"
39 | #ifdef LINUX
40 | #include
41 | #endif
42 | }
43 |
44 | #ifdef DARWIN
45 | #include
46 | #include
47 | #else
48 | #include
49 | #include
50 | #endif
51 |
52 | const float kFeetToMeters = 0.3048;
53 |
54 | Vector4f s_clearColor(0.0, 0, 0.3, 1);
55 |
56 | // time tracking
57 | unsigned int s_ticks = 0;
58 | unsigned int s_frames = 0;
59 | static Matrixf s_RotY90 = Matrixf::AxisAngle(Vector3f(0,1,0), PI/2.0f);
60 |
61 | ovrHmd s_hmd;
62 | ovrEyeRenderDesc s_eyeRenderDesc[2];
63 | ovrSizei s_renderTargetSize;
64 | ovrTexture s_eyeTexture[2];
65 |
66 | uint32_t s_fbo;
67 | uint32_t s_fboDepth;
68 | uint32_t s_fboTex;
69 |
70 | // TODO: get height from ovr SDK
71 | Vector3f s_cameraPos(0, 6 * kFeetToMeters, 0);
72 |
73 | void TermConfigInit()
74 | {
75 | init_config();
76 |
77 | // load config from ~/.riftty
78 | struct passwd *pw = getpwuid(getuid());
79 | const char *homedir = pw->pw_dir;
80 | char config_filename[512];
81 | strncpy(config_filename, homedir, 512);
82 | strncat(config_filename, "/.riftty", 512);
83 | load_config(config_filename);
84 |
85 | cs_init();
86 |
87 | finish_config();
88 | win_reconfig();
89 | }
90 |
91 | void DumpHMDInfo(ovrHmd hmd)
92 | {
93 | printf("HMDInfo\n");
94 | char* type = (char*)"???";
95 | switch (hmd->Type) {
96 | case ovrHmd_None: type = (char*)"None"; break;
97 | case ovrHmd_DK1: type = (char*)"DK1"; break;
98 | case ovrHmd_DKHD: type = (char*)"DKHD"; break;
99 | case ovrHmd_DK2: type = (char*)"DK2"; break;
100 | case ovrHmd_BlackStar: type = (char*)"BlackStar"; break;
101 | case ovrHmd_CB: type = (char*)"CB"; break;
102 | case ovrHmd_Other: type = (char*)"Other"; break;
103 | }
104 | printf(" Type = %s\n", type);
105 | printf(" ProductName = \"%s\"\n", hmd->ProductName);
106 | printf(" Manufacturer = \"%s\"\n", hmd->Manufacturer);
107 | printf(" VendorId = %d\n", hmd->VendorId);
108 | printf(" ProductId = %d\n", hmd->VendorId);
109 | printf(" SerialNumber = ");
110 | for (int i = 0; i < 24; i++)
111 | {
112 | printf("%c", hmd->SerialNumber[i]);
113 | }
114 | printf("\n");
115 | printf(" FirmwareVersion = %d.%d\n", hmd->FirmwareMajor, hmd->FirmwareMinor);
116 | printf(" CamraFrustum\n");
117 | printf(" HFov = %0.2f\xC2\xB0\n", RadToDeg(hmd->CameraFrustumHFovInRadians));
118 | printf(" VFov = %0.2f\xC2\xB0\n", RadToDeg(hmd->CameraFrustumVFovInRadians));
119 | printf(" NearZ = %0.2fm\n", hmd->CameraFrustumNearZInMeters);
120 | printf(" FarZ = %0.2fm\n", hmd->CameraFrustumFarZInMeters);
121 |
122 | printf(" HmdCaps = 0x%x\n", hmd->HmdCaps);
123 | printf(" Present = %s\n", hmd->HmdCaps & ovrHmdCap_Present ? "TRUE" : "FALSE");
124 | printf(" Available = %s\n", hmd->HmdCaps & ovrHmdCap_Available ? "TRUE" : "FALSE");
125 | printf(" Captured = %s\n", hmd->HmdCaps & ovrHmdCap_Captured ? "TRUE" : "FALSE");
126 | printf(" ExtendDesktop = %s\n", hmd->HmdCaps & ovrHmdCap_ExtendDesktop ? "TRUE" : "FALSE");
127 | printf(" NoMirrorToWindow = %s\n", hmd->HmdCaps & ovrHmdCap_NoMirrorToWindow ? "TRUE" : "FALSE");
128 | printf(" DisplayOff = %s\n", hmd->HmdCaps & ovrHmdCap_DisplayOff ? "TRUE" : "FALSE");
129 | printf(" LowPersistence = %s\n", hmd->HmdCaps & ovrHmdCap_LowPersistence ? "TRUE" : "FALSE");
130 | printf(" DynamicPrediction = %s\n", hmd->HmdCaps & ovrHmdCap_DynamicPrediction ? "TRUE" : "FALSE");
131 | printf(" DynamicNoVSync = %s\n", hmd->HmdCaps & ovrHmdCap_NoVSync ? "TRUE" : "FALSE");
132 |
133 | printf(" TrackingCaps = 0x%x\n", hmd->TrackingCaps);
134 | printf(" Orientation = %s\n", hmd->TrackingCaps & ovrTrackingCap_Orientation ? "TRUE" : "FALSE");
135 | printf(" MagbYawCorrection = %s\n", hmd->TrackingCaps & ovrTrackingCap_MagYawCorrection ? "TRUE" : "FALSE");
136 | printf(" Position = %s\n", hmd->TrackingCaps & ovrTrackingCap_Position ? "TRUE" : "FALSE");
137 |
138 | printf(" DistortionCaps = 0x%x\n", hmd->DistortionCaps);
139 | printf(" TimeWarp = %s\n", hmd->DistortionCaps & ovrDistortionCap_TimeWarp ? "TRUE" : "FALSE");
140 | printf(" Vignette = %s\n", hmd->DistortionCaps & ovrDistortionCap_Vignette ? "TRUE" : "FALSE");
141 | printf(" NoRestore = %s\n", hmd->DistortionCaps & ovrDistortionCap_NoRestore ? "TRUE" : "FALSE");
142 | printf(" FlipInput = %s\n", hmd->DistortionCaps & ovrDistortionCap_FlipInput ? "TRUE" : "FALSE");
143 | printf(" SRGB = %s\n", hmd->DistortionCaps & ovrDistortionCap_SRGB ? "TRUE" : "FALSE");
144 | printf(" Overdrive = %s\n", hmd->DistortionCaps & ovrDistortionCap_Overdrive ? "TRUE" : "FALSE");
145 | printf(" HqDistortion = %s\n", hmd->DistortionCaps & ovrDistortionCap_HqDistortion ? "TRUE" : "FALSE");
146 | printf(" LinuxDevFullscreen = %s\n", hmd->DistortionCaps & ovrDistortionCap_LinuxDevFullscreen ? "TRUE" : "FALSE");
147 | printf(" ComputeShader = %s\n", hmd->DistortionCaps & ovrDistortionCap_ComputeShader ? "TRUE" : "FALSE");
148 | printf(" TimewarpJitDelay = %s\n", hmd->DistortionCaps & ovrDistortionCap_TimewarpJitDelay ? "TRUE" : "FALSE");
149 | printf(" ProfileNoSpinWaits = %s\n", hmd->DistortionCaps & ovrDistortionCap_ProfileNoSpinWaits ? "TRUE" : "FALSE");
150 |
151 | printf(" DefaultEyeFov = [(%f, %f, %f, %f), (%f, %f, %f, %f])]\n",
152 | hmd->DefaultEyeFov[0].UpTan, hmd->DefaultEyeFov[0].DownTan, hmd->DefaultEyeFov[0].LeftTan, hmd->DefaultEyeFov[0].RightTan,
153 | hmd->DefaultEyeFov[1].UpTan, hmd->DefaultEyeFov[1].DownTan, hmd->DefaultEyeFov[1].LeftTan, hmd->DefaultEyeFov[1].RightTan);
154 | printf(" MaxEyeFov = [(%f, %f, %f, %f), (%f, %f, %f, %f])]\n",
155 | hmd->MaxEyeFov[0].UpTan, hmd->MaxEyeFov[0].DownTan, hmd->MaxEyeFov[0].LeftTan, hmd->MaxEyeFov[0].RightTan,
156 | hmd->MaxEyeFov[1].UpTan, hmd->MaxEyeFov[1].DownTan, hmd->MaxEyeFov[1].LeftTan, hmd->MaxEyeFov[1].RightTan);
157 | printf(" EyeRenderOrder = %s, %s\n", hmd->EyeRenderOrder[0] == ovrEye_Left ? "Left" : "Right", hmd->EyeRenderOrder[1] == ovrEye_Left ? "Left" : "Right");
158 |
159 | printf(" Resolution = %d x %d\n", hmd->Resolution.w, hmd->Resolution.h);
160 | printf(" WindowsPos = (%d, %d)\n", hmd->WindowsPos.x, hmd->WindowsPos.y);
161 |
162 | printf(" DisplayDeviceName = %s\n", hmd->DisplayDeviceName);
163 | printf(" DisplayId = %d\n", hmd->DisplayId);
164 | }
165 |
166 | void CreateRenderTarget(int width, int height);
167 |
168 | void RiftInit()
169 | {
170 | ovr_Initialize();
171 |
172 | s_hmd = ovrHmd_Create(0);
173 | if (!s_hmd)
174 | {
175 | s_hmd = ovrHmd_CreateDebug(ovrHmd_DK1);
176 | }
177 |
178 | DumpHMDInfo(s_hmd);
179 | }
180 |
181 | void TermInit()
182 | {
183 | win_init();
184 |
185 | // TODO: get SHELL from env
186 |
187 | cs_reconfig(); // TODO: do not want
188 |
189 | term_init();
190 | term_reset();
191 | term_resize(cfg.rows, cfg.cols);
192 |
193 | // TODO:
194 | int font_width = 10;
195 | int font_height = 10;
196 | unsigned short term_width = font_width * cfg.cols;
197 | unsigned short term_height = font_height * cfg.rows;
198 |
199 | #ifdef DARWIN
200 | char login[128];
201 | char* username = getlogin();
202 | if (!username) {
203 | username = getenv("LOGNAME");
204 | if (!username) {
205 | fprintf(stderr, "ERROR: Could not determine username, try setting LOGNAME environment variable\n");
206 | exit(1);
207 | }
208 | }
209 | strncpy(login, username, 128);
210 |
211 | const char* login_argv[] = {"login", "-pfl", login, NULL};
212 | #endif
213 | #ifdef LINUX
214 | char* env_shell = getenv("SHELL");
215 | const struct passwd *pass = getpwuid(getuid());
216 | if (pass) {
217 | setenv("LOGNAME", pass->pw_name, 1);
218 | setenv("USER", pass->pw_name, 1);
219 | setenv("SHELL", pass->pw_shell, 0);
220 | setenv("HOME", pass->pw_dir, 0);
221 | }
222 | char* shell = env_shell ? env_shell : pass->pw_shell;
223 | if (!shell) {
224 | fprintf(stderr, "ERROR: could not determine shell to use, try setting SHELL environment variable\n");
225 | exit(2);
226 | }
227 | const char* login_argv[] = {shell, "-i", NULL};
228 | #endif
229 |
230 | unsigned short rows = cfg.rows;
231 | unsigned short cols = cfg.cols;
232 | winsize ws = {rows, cols, term_width, term_height};
233 | child_create(login_argv, &ws);
234 | }
235 |
236 | void TermShutdown()
237 | {
238 | child_kill(true);
239 | win_shutdown();
240 | }
241 |
242 | void RiftConfigure()
243 | {
244 | uint32_t supportedSensorCaps = ovrTrackingCap_Orientation | ovrTrackingCap_MagYawCorrection | ovrTrackingCap_Position;
245 | uint32_t requiredTrackingCaps = 0;
246 |
247 | ovrBool success = ovrHmd_ConfigureTracking(s_hmd, supportedSensorCaps, requiredTrackingCaps);
248 | if (!success) {
249 | fprintf(stderr, "ERROR: HMD does not have required capabilities!\n");
250 | exit(2);
251 | }
252 |
253 | // Figure out dimensions of render target
254 | ovrSizei recommenedTex0Size = ovrHmd_GetFovTextureSize(s_hmd, ovrEye_Left, s_hmd->DefaultEyeFov[0], 1.0f);
255 | ovrSizei recommenedTex1Size = ovrHmd_GetFovTextureSize(s_hmd, ovrEye_Right, s_hmd->DefaultEyeFov[1], 1.0f);
256 | s_renderTargetSize.w = recommenedTex0Size.w + recommenedTex1Size.w;
257 | s_renderTargetSize.h = std::max(recommenedTex0Size.h, recommenedTex1Size.h);
258 |
259 | CreateRenderTarget(s_renderTargetSize.w, s_renderTargetSize.h);
260 |
261 | s_eyeTexture[0].Header.API = ovrRenderAPI_OpenGL;
262 | s_eyeTexture[0].Header.TextureSize = s_renderTargetSize;
263 | s_eyeTexture[0].Header.RenderViewport.Pos = {0, 0};
264 | s_eyeTexture[0].Header.RenderViewport.Size = {s_renderTargetSize.w / 2, s_renderTargetSize.h};
265 | ((ovrGLTexture*)(&s_eyeTexture[0]))->OGL.TexId = s_fboTex;
266 |
267 | s_eyeTexture[1].Header.API = ovrRenderAPI_OpenGL;
268 | s_eyeTexture[1].Header.TextureSize = s_renderTargetSize;
269 | s_eyeTexture[1].Header.RenderViewport.Pos = {s_renderTargetSize.w / 2, 0};
270 | s_eyeTexture[1].Header.RenderViewport.Size = {s_renderTargetSize.w / 2, s_renderTargetSize.h};
271 | ((ovrGLTexture*)(&s_eyeTexture[1]))->OGL.TexId = s_fboTex;
272 |
273 | // Configure ovr SDK Rendering
274 | ovrGLConfig conf;
275 | memset(&conf, 0, sizeof(ovrGLConfig));
276 | conf.OGL.Header.API = ovrRenderAPI_OpenGL;
277 | #ifdef LINUX
278 | conf.OGL.Header.BackBufferSize = {s_hmd->Resolution.h, s_hmd->Resolution.w};
279 | #else
280 | conf.OGL.Header.BackBufferSize = {s_hmd->Resolution.w, s_hmd->Resolution.h};
281 | #endif
282 | conf.OGL.Header.Multisample = 0;
283 | // TODO: on windows need to set HWND, on Linux need to set other parameters
284 | if (!ovrHmd_ConfigureRendering(s_hmd, &conf.Config, s_hmd->DistortionCaps & ~ovrDistortionCap_FlipInput, s_hmd->DefaultEyeFov, s_eyeRenderDesc))
285 | {
286 | fprintf(stderr, "ERROR: HMD configure rendering failed!\n");
287 | exit(3);
288 | }
289 |
290 | ovrHmd_DismissHSWDisplay(s_hmd);
291 | }
292 |
293 | void RiftShutdown()
294 | {
295 | ovrHmd_Destroy(s_hmd);
296 | ovr_Shutdown();
297 | }
298 |
299 | void Process(float dt)
300 | {
301 | float kSpeed = 1.5f;
302 | Joystick* joy = JOYSTICK_GetJoystick();
303 | Vector3f vel(joy->axes[Joystick::LeftStickX],
304 | joy->axes[Joystick::RightStickY],
305 | -joy->axes[Joystick::LeftStickY]);
306 | s_cameraPos = s_cameraPos + vel * dt * kSpeed;
307 |
308 | child_poll();
309 | }
310 |
311 | void Render(float dt)
312 | {
313 | static unsigned int frameIndex = 0;
314 | frameIndex++;
315 | ovrFrameTiming timing = ovrHmd_BeginFrame(s_hmd, 0);
316 |
317 | // TODO: Use player height from SDK
318 |
319 | glBindBuffer(GL_ARRAY_BUFFER, 0);
320 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
321 |
322 | // render into fbo
323 | glBindFramebuffer(GL_FRAMEBUFFER, s_fbo);
324 |
325 | // TODO: enable this when we have more complex rendering.
326 | glEnable(GL_DEPTH_TEST);
327 | glDisable(GL_CULL_FACE);
328 | glEnable(GL_BLEND);
329 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
330 |
331 | static float t = 0.0;
332 | t += dt;
333 |
334 | // clear render target
335 | glViewport(0, 0, s_renderTargetSize.w, s_renderTargetSize.h);
336 | glClearColor(s_clearColor.x, s_clearColor.y, s_clearColor.z, s_clearColor.w);
337 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
338 |
339 | ovrTrackingState ts = ovrHmd_GetTrackingState(s_hmd, ovr_GetTimeInSeconds());
340 |
341 | Vector3f posTrackOffset(0.0f, 0.0f, 0.0f);
342 | if (ts.StatusFlags & (ovrStatus_PositionTracked)) {
343 | ovrPosef pose = ts.HeadPose.ThePose;
344 | posTrackOffset.Set(pose.Position.x, pose.Position.y, pose.Position.z);
345 | }
346 |
347 | ovrPosef headPose[2];
348 |
349 | for (int i = 0; i < 2; i++)
350 | {
351 | ovrEyeType eye = s_hmd->EyeRenderOrder[i];
352 | ovrPosef pose = ovrHmd_GetHmdPosePerEye(s_hmd, eye);
353 | headPose[eye] = pose;
354 |
355 | glViewport(s_eyeTexture[eye].Header.RenderViewport.Pos.x,
356 | s_eyeTexture[eye].Header.RenderViewport.Pos.y,
357 | s_eyeTexture[eye].Header.RenderViewport.Size.w,
358 | s_eyeTexture[eye].Header.RenderViewport.Size.h);
359 |
360 | Quatf q(pose.Orientation.x, pose.Orientation.y, pose.Orientation.z, pose.Orientation.w);
361 | Vector3f p(pose.Position.x, pose.Position.y, pose.Position.z);
362 |
363 | Matrixf cameraMatrix = Matrixf::QuatTrans(q, s_cameraPos + posTrackOffset);
364 | Matrixf viewCenter = cameraMatrix.OrthoInverse();
365 |
366 | // let ovr compute projection matrix, cause it's hard.
367 | ovrMatrix4f ovrProj = ovrMatrix4f_Projection(s_eyeRenderDesc[eye].Fov, 0.1f, 10000.0f, true);
368 |
369 | // convert to abaci matrix
370 | Matrixf projMatrix = Matrixf::Rows(Vector4f(ovrProj.M[0][0], ovrProj.M[0][1], ovrProj.M[0][2], ovrProj.M[0][3]),
371 | Vector4f(ovrProj.M[1][0], ovrProj.M[1][1], ovrProj.M[1][2], ovrProj.M[1][3]),
372 | Vector4f(ovrProj.M[2][0], ovrProj.M[2][1], ovrProj.M[2][2], ovrProj.M[2][3]),
373 | Vector4f(ovrProj.M[3][0], ovrProj.M[3][1], ovrProj.M[3][2], ovrProj.M[3][3]));
374 |
375 | // use EyeRenderDesc.ViewAdjust to do eye offset.
376 | Matrixf viewMatrix = viewCenter * Matrixf::Trans(Vector3f(s_eyeRenderDesc[eye].HmdToEyeViewOffset.x,
377 | s_eyeRenderDesc[eye].HmdToEyeViewOffset.y,
378 | s_eyeRenderDesc[eye].HmdToEyeViewOffset.z));
379 |
380 | RenderBegin();
381 |
382 | RenderFloor(projMatrix, viewMatrix, 0.0f);
383 |
384 | // compute model matrix for terminal
385 | const Vector3f termOrigin(cfg.win_pos.x, cfg.win_pos.y, cfg.win_pos.z);
386 |
387 | const float termWidth = win_get_max_advance() * cfg.cols;
388 | const float termHeight = win_get_line_height() * cfg.rows;
389 |
390 | const float termScale = cfg.win_width / termWidth;
391 |
392 | const Quatf rot = (Quatf::AxisAngle(Vector3f(1, 0, 0), DegToRad(cfg.win_rot.x)) *
393 | Quatf::AxisAngle(Vector3f(0, 1, 0), DegToRad(cfg.win_rot.y)) *
394 | Quatf::AxisAngle(Vector3f(0, 0, 1), DegToRad(cfg.win_rot.z)));
395 |
396 | const Vector3f anchorOffset = Vector3f(cfg.win_anchor.x, cfg.win_anchor.y, 0) * Vector3f(termWidth, termHeight, 0);
397 |
398 | Matrixf anchorMatrix = Matrixf::Trans(-anchorOffset);
399 | Matrixf posMat = Matrixf::Trans(termOrigin);
400 | const Vector3f scale(termScale, -termScale, termScale);
401 | Matrixf modelMatrix = posMat * Matrixf::ScaleQuatTrans(scale, rot, Vector3f(0,0,0)) * anchorMatrix;
402 |
403 | //Matrixf modelMatrix = Matrixf::ScaleQuatTrans(scale, rot, termOrigin);
404 |
405 |
406 | RenderTextBegin(projMatrix, viewMatrix, modelMatrix);
407 | for (int j = 0; j < win_get_text_count(); j++)
408 | {
409 | gb::Text* text = (gb::Text*)win_get_text(j);
410 | if (text)
411 | {
412 | RenderText(text->GetQuadVec());
413 | }
414 | }
415 | RenderTextEnd();
416 |
417 | RenderEnd();
418 | }
419 |
420 | ovrHmd_EndFrame(s_hmd, headPose, s_eyeTexture);
421 | }
422 |
423 | void CreateRenderTarget(int width, int height)
424 | {
425 | printf("Render Target size %d x %d\n", width, height);
426 |
427 | // create a fbo
428 | glGenFramebuffers(1, &s_fbo);
429 |
430 | GL_ERROR_CHECK("RenderTargetInit1");
431 |
432 | // create a depth buffer
433 | glGenRenderbuffers(1, &s_fboDepth);
434 |
435 | GL_ERROR_CHECK("RenderTargetInit2");
436 |
437 | // bind fbo
438 | glBindFramebuffer(GL_FRAMEBUFFER, s_fbo);
439 |
440 | GL_ERROR_CHECK("RenderTargetInit3");
441 |
442 | glGenTextures(1, &s_fboTex);
443 | glBindTexture(GL_TEXTURE_2D, s_fboTex);
444 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
445 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
446 |
447 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
448 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
449 |
450 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
451 |
452 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
453 |
454 | // attach texture to fbo
455 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
456 | GL_TEXTURE_2D, s_fboTex, 0);
457 |
458 | // init depth buffer
459 | glBindRenderbuffer(GL_RENDERBUFFER, s_fboDepth);
460 |
461 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
462 |
463 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, s_fboDepth);
464 |
465 | // check status
466 | assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
467 |
468 | GL_ERROR_CHECK("RenderTargetInit");
469 | }
470 |
471 | static bool s_needsRestart = false;
472 | void RestartHandler(int signal)
473 | {
474 | printf("NEEDS RESTART! pid = %d\n", getpid());
475 | // NOTE: I could call execvp directly here, but the process will
476 | // inherit the signal mask which will not let me hook up the SIGUSR1 handler.
477 | // So we set a flag instead.
478 | s_needsRestart = true;
479 | }
480 |
481 | void Restart()
482 | {
483 | printf("RESTART! pid = %d\n", getpid());
484 | char* argv[2];
485 | argv[0] = (char*)"./riftty";
486 | argv[1] = NULL;
487 | int err = execvp(*argv, argv);
488 | if (err == -1)
489 | perror("execvp");
490 | }
491 |
492 | int main(int argc, char* argv[])
493 | {
494 | printf("START! pid = %d\n", getpid());
495 |
496 | if (!system("./restart.rb"))
497 | exit(1);
498 |
499 | if (signal(SIGUSR1, RestartHandler) == SIG_ERR) {
500 | fprintf(stderr, "An error occurred while setting a signal handler.\n");
501 | exit(1);
502 | }
503 |
504 | TermConfigInit();
505 | RiftInit();
506 |
507 | SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
508 | SDL_Window* displayWindow;
509 | SDL_Renderer* displayRenderer;
510 |
511 | uint32_t flags = SDL_WINDOW_OPENGL | (cfg.win_fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
512 |
513 | #ifdef LINUX
514 | int err = SDL_CreateWindowAndRenderer(s_hmd->Resolution.h, s_hmd->Resolution.w, flags, &displayWindow, &displayRenderer);
515 | #else
516 | int err = SDL_CreateWindowAndRenderer(s_hmd->Resolution.w, s_hmd->Resolution.h, flags, &displayWindow, &displayRenderer);
517 | #endif
518 | if (err == -1 || !displayWindow || !displayRenderer) {
519 | fprintf(stderr, "SDL_CreateWindowAndRenderer failed!\n");
520 | }
521 |
522 | SDL_DisplayMode currentMode;
523 | SDL_GetWindowDisplayMode(displayWindow, ¤tMode);
524 |
525 | SDL_RendererInfo displayRendererInfo;
526 | SDL_GetRendererInfo(displayRenderer, &displayRendererInfo);
527 | /* TODO: Check that we have OpenGL */
528 | if ((displayRendererInfo.flags & SDL_RENDERER_ACCELERATED) == 0 ||
529 | (displayRendererInfo.flags & SDL_RENDERER_TARGETTEXTURE) == 0) {
530 | /* TODO: Handle this. We have no render surface and not accelerated. */
531 | fprintf(stderr, "NO RENDERER wtf!\n");
532 | }
533 |
534 | atexit(SDL_Quit);
535 |
536 | JOYSTICK_Init();
537 |
538 | RiftConfigure();
539 | RenderInit();
540 | TermInit();
541 |
542 | bool done = false;
543 | while (!done)
544 | {
545 | JOYSTICK_ClearFlags();
546 |
547 | SDL_Event event;
548 | while (SDL_PollEvent(&event))
549 | {
550 | switch (event.type)
551 | {
552 | case SDL_QUIT:
553 | done = true;
554 | break;
555 |
556 | case SDL_MOUSEMOTION:
557 | if (event.motion.state & SDL_BUTTON(1)) {
558 | // move touch
559 | }
560 | break;
561 |
562 | case SDL_MOUSEBUTTONDOWN:
563 | if (event.button.button == SDL_BUTTON_LEFT) {
564 | // start touch
565 | }
566 | break;
567 |
568 | case SDL_MOUSEBUTTONUP:
569 | if (event.button.button == SDL_BUTTON_LEFT) {
570 | // end touch
571 | }
572 | break;
573 |
574 | case SDL_JOYAXISMOTION:
575 | JOYSTICK_UpdateMotion(&event.jaxis);
576 | break;
577 |
578 | case SDL_JOYBUTTONDOWN:
579 | case SDL_JOYBUTTONUP:
580 | JOYSTICK_UpdateButton(&event.jbutton);
581 | break;
582 |
583 | case SDL_KEYDOWN:
584 | case SDL_KEYUP:
585 | if (ProcessKeyEvent(&event.key)) {
586 | done = true;
587 | }
588 | break;
589 | }
590 | }
591 |
592 | if (!done)
593 | {
594 | if (s_needsRestart)
595 | Restart();
596 |
597 | unsigned int now = SDL_GetTicks(); // milliseconds
598 | float dt = (now - s_ticks) / 1000.0f; // convert to seconds.
599 | s_ticks = now;
600 |
601 | //printf("fps = %.0f\n", 1.0f/dt);
602 |
603 | Process(dt);
604 | Render(dt);
605 |
606 | s_frames++;
607 | }
608 | }
609 |
610 | TermShutdown();
611 | RiftShutdown();
612 | JOYSTICK_Shutdown();
613 |
614 | return 0;
615 | }
616 |
--------------------------------------------------------------------------------
/shader.cpp:
--------------------------------------------------------------------------------
1 | #include "shader.h"
2 | #include
3 | #include
4 |
5 | // helper used for shader loading
6 | enum LoadFileToMemoryResult { CouldNotOpenFile = -1, CouldNotReadFile = -2 };
7 | static int _LoadFileToMemory(const char *filename, char **result)
8 | {
9 | int size = 0;
10 | FILE *f = fopen(filename, "rb");
11 | if (f == NULL)
12 | {
13 | *result = NULL;
14 | return CouldNotOpenFile;
15 | }
16 | fseek(f, 0, SEEK_END);
17 | size = ftell(f);
18 | fseek(f, 0, SEEK_SET);
19 | *result = new char[size + 1];
20 |
21 | int newSize = (int)fread(*result, sizeof(char), size, f);
22 | if (size != newSize)
23 | {
24 | fprintf(stderr, "size mismatch, size = %d, newSize = %d\n", size, newSize);
25 | delete [] *result;
26 | return CouldNotReadFile;
27 | }
28 | fclose(f);
29 | (*result)[size] = 0; // make sure it's null terminated.
30 | return size;
31 | }
32 |
33 | static bool _CompileShader(GLint* shader, GLenum type, const char* source, int size)
34 | {
35 | *shader = glCreateShader(type);
36 | glShaderSource(*shader, 1, (const GLchar**)&source, &size);
37 | glCompileShader(*shader);
38 |
39 | GLint compiled;
40 | glGetShaderiv(*shader, GL_COMPILE_STATUS, &compiled);
41 | return compiled;
42 | }
43 |
44 | static void _DumpShaderInfoLog(GLint shader)
45 | {
46 | GLint bufferLen = 0;
47 | glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &bufferLen);
48 | if (bufferLen > 1)
49 | {
50 | GLsizei len = 0;
51 | char* buffer = new char[bufferLen];
52 | glGetShaderInfoLog(shader, bufferLen, &len, buffer);
53 | fprintf(stderr, "glGetShaderInfoLog() = \n");
54 | fprintf(stderr, "%s", buffer);
55 | delete [] buffer;
56 | }
57 | }
58 |
59 | static void _DumpProgramInfoLog(GLint prog)
60 | {
61 | const GLint bufferLen = 4096;
62 | GLsizei len = 0;
63 | char* buffer = new char[bufferLen];
64 | glGetProgramInfoLog(prog, bufferLen, &len, buffer);
65 | if (len > 0)
66 | {
67 | fprintf(stderr, "glGetProgramInfoLog() = \n");
68 | fprintf(stderr, "%s", buffer);
69 | delete [] buffer;
70 | }
71 | }
72 |
73 | static bool _Link(GLint *program, GLint vertShader, GLint fragShader)
74 | {
75 | assert(vertShader && fragShader);
76 |
77 | *program = glCreateProgram();
78 | glAttachShader(*program, vertShader);
79 | glAttachShader(*program, fragShader);
80 | glLinkProgram(*program);
81 |
82 | GLint linked;
83 | glGetProgramiv(*program, GL_LINK_STATUS, &linked);
84 | return linked;
85 | }
86 |
87 | static int s_maxVertexAttribs = 0;
88 |
89 | Shader::Shader() : m_vertShader(0), m_fragShader(0), m_program(0)
90 | {
91 | // HACK: should probably do this in a renderer object
92 | if (s_maxVertexAttribs == 0)
93 | {
94 | glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &s_maxVertexAttribs);
95 | }
96 | }
97 |
98 | Shader::~Shader()
99 | {
100 | if (m_vertShader)
101 | glDeleteShader(m_vertShader);
102 | if (m_fragShader)
103 | glDeleteShader(m_fragShader);
104 | if (m_program)
105 | glDeleteProgram(m_program);
106 | }
107 |
108 | bool Shader::compileAndLinkFromFiles(const std::string& vertShaderFilename,
109 | const std::string& fragShaderFilename)
110 | {
111 | char* vertShaderSource;
112 | int vertShaderSize = _LoadFileToMemory(vertShaderFilename.c_str(), &vertShaderSource);
113 | if (vertShaderSize <= 0)
114 | {
115 | fprintf(stderr, "Shader::compileAndLinkFromFiles() Failed to load vertex shader \"%s\"\n", vertShaderFilename.c_str());
116 | return false;
117 | }
118 |
119 | char* fragShaderSource;
120 | int fragShaderSize = _LoadFileToMemory(fragShaderFilename.c_str(), &fragShaderSource);
121 | if (fragShaderSize <= 0)
122 | {
123 | fprintf(stderr, "Shader::compileAndLinkFromFiles() Failed to load fragment shader \"%s\"\n", fragShaderFilename.c_str());
124 | delete [] vertShaderSource;
125 | return false;
126 | }
127 |
128 | if (!_CompileShader(&m_vertShader, GL_VERTEX_SHADER, vertShaderSource, vertShaderSize))
129 | {
130 | fprintf(stderr, "Shader::compileAndLinkFromFiles() Failed to compile vertex shader \"%s\"\n", vertShaderFilename.c_str());
131 | _DumpShaderInfoLog(m_vertShader);
132 | glDeleteShader(m_vertShader);
133 | m_vertShader = 0;
134 | return false;
135 | }
136 |
137 | if (!_CompileShader(&m_fragShader, GL_FRAGMENT_SHADER, fragShaderSource, fragShaderSize))
138 | {
139 | fprintf(stderr, "Shader::compileAndLinkFromFiles() Failed to compile fragment shader \"%s\"\n", fragShaderFilename.c_str());
140 | _DumpShaderInfoLog(m_fragShader);
141 | glDeleteShader(m_fragShader);
142 | m_fragShader = 0;
143 | return false;
144 | }
145 |
146 | if (!_Link(&m_program, m_vertShader, m_fragShader))
147 | {
148 | fprintf(stderr, "Shader::compileAndLinkFromFiles() Failed to link shaders \"%s\" and \"%s\"\n", vertShaderFilename.c_str(), fragShaderFilename.c_str());
149 | _DumpProgramInfoLog(m_program);
150 | glDeleteProgram(m_program);
151 | m_program = 0;
152 | return false;
153 | }
154 |
155 | m_vertShaderFilename = vertShaderFilename;
156 | m_fragShaderFilename = fragShaderFilename;
157 |
158 | buildVarMaps();
159 | return true;
160 | }
161 |
162 | int Shader::getUniformLoc(const std::string& uniformName) const
163 | {
164 | VarMap::const_iterator iter = m_uniformVarMap.find(uniformName);
165 | if (iter != m_uniformVarMap.end())
166 | return iter->second.loc;
167 | else
168 | return -1;
169 | }
170 |
171 | int Shader::getAttribLoc(const std::string& attribName) const
172 | {
173 | VarMap::const_iterator iter = m_attribVarMap.find(attribName);
174 | if (iter != m_attribVarMap.end())
175 | return iter->second.loc;
176 | else
177 | return -1;
178 | }
179 |
180 | GLint Shader::getProgram() const
181 | {
182 | return m_program;
183 | }
184 |
185 | void Shader::buildVarMaps()
186 | {
187 | const int MAX_STRING_SIZE = 1024;
188 | char tempStr[MAX_STRING_SIZE];
189 | int numUniforms = 0;
190 | int numAttribs = 0;
191 | Var v;
192 |
193 | glGetProgramiv(m_program, GL_ACTIVE_UNIFORMS, &numUniforms);
194 | for (int i = 0; i < numUniforms; ++i)
195 | {
196 | GLsizei strLen;
197 | glGetActiveUniform(m_program, i, MAX_STRING_SIZE, &strLen, &v.size, &v.type, tempStr);
198 | v.loc = glGetUniformLocation(m_program, tempStr);
199 | m_uniformVarMap[std::string(tempStr)] = v;
200 | }
201 |
202 | m_attribLocMask = 0;
203 | glGetProgramiv(m_program, GL_ACTIVE_ATTRIBUTES, &numAttribs);
204 | for (int i = 0; i < numAttribs; ++i)
205 | {
206 | GLsizei strLen;
207 | glGetActiveAttrib(m_program, i, MAX_STRING_SIZE, &strLen, &v.size, &v.type, tempStr);
208 | v.loc = glGetAttribLocation(m_program, tempStr);
209 | m_attribVarMap[std::string(tempStr)] = v;
210 | m_attribLocMask |= ((uint32_t)1 << v.loc);
211 | assert(v.loc < 32);
212 | }
213 |
214 | dump();
215 | }
216 |
217 | void Shader::apply(const Shader* prev) const
218 | {
219 | if (prev && prev->m_program == m_program)
220 | return;
221 |
222 | assert(m_program);
223 | glUseProgram(m_program);
224 |
225 | uint32_t diff = m_attribLocMask;
226 | if (prev)
227 | diff = m_attribLocMask ^ prev->m_attribLocMask;
228 | else
229 | diff = -1; // assume the worst!
230 |
231 | if (diff) {
232 | const int kNumVertexAttribs = std::min(s_maxVertexAttribs, 32);
233 | for (int i = 0; i < kNumVertexAttribs; i++) {
234 | if (diff & (1 << i)) {
235 | if (m_attribLocMask & (1 << i)) {
236 | glEnableVertexAttribArray(i);
237 | } else {
238 | glDisableVertexAttribArray(i);
239 | }
240 | }
241 | }
242 | }
243 | }
244 |
245 | void Shader::dump() const
246 | {
247 | printf("Shader \"%s\", \"%s\"\n", m_vertShaderFilename.c_str(), m_fragShaderFilename.c_str());
248 |
249 | printf(" uniforms\n");
250 | VarMap::const_iterator uIter = m_uniformVarMap.begin();
251 | VarMap::const_iterator uEnd = m_uniformVarMap.end();
252 | for (; uIter != uEnd; uIter++)
253 | {
254 | printf(" \"%s\": loc = %d, type = %d, size = %d\n",
255 | uIter->first.c_str(), uIter->second.loc, uIter->second.type, uIter->second.size);
256 | }
257 |
258 | printf(" attribs\n");
259 | uIter = m_attribVarMap.begin();
260 | uEnd = m_attribVarMap.end();
261 | for (; uIter != uEnd; uIter++)
262 | {
263 | printf(" \"%s\": loc = %d, type = %d, size = %d\n",
264 | uIter->first.c_str(), uIter->second.loc, uIter->second.type, uIter->second.size);
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/shader.h:
--------------------------------------------------------------------------------
1 | #ifndef __SHADER_H__
2 | #define __SHADER_H__
3 |
4 | #include
5 | #include