├── .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 | ![](https://raw.github.com/hyperlogic/riftty/master/docs/screenshot.png) 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 6 | #include "opengl.h" 7 | 8 | class Shader 9 | { 10 | public: 11 | Shader(); 12 | virtual ~Shader(); 13 | bool compileAndLinkFromFiles(const std::string& vertShaderFilename, 14 | const std::string& fragShaderFilename); 15 | int getUniformLoc(const std::string& uniformName) const; 16 | int getAttribLoc(const std::string& attribName) const; 17 | GLint getProgram() const; 18 | void apply(const Shader* prev) const; 19 | void dump() const; 20 | protected: 21 | void buildVarMaps(); 22 | 23 | std::string m_vertShaderFilename; 24 | std::string m_fragShaderFilename; 25 | 26 | GLint m_vertShader; 27 | GLint m_fragShader; 28 | GLint m_program; 29 | 30 | struct Var 31 | { 32 | GLint size; 33 | GLenum type; 34 | GLint loc; 35 | }; 36 | 37 | typedef std::map VarMap; 38 | VarMap m_uniformVarMap; 39 | VarMap m_attribVarMap; 40 | uint32_t m_attribLocMask; 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /shader/fullbright.fsh: -------------------------------------------------------------------------------- 1 | uniform vec4 color; 2 | 3 | void main(void) 4 | { 5 | gl_FragColor = color; 6 | } 7 | -------------------------------------------------------------------------------- /shader/fullbright.vsh: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // No lighting at all, solid color 4 | // 5 | 6 | uniform vec4 color; 7 | uniform mat4 mat; 8 | attribute vec3 pos; 9 | 10 | void main(void) 11 | { 12 | gl_Position = mat * vec4(pos, 1); 13 | } 14 | -------------------------------------------------------------------------------- /shader/fullbright_textured.fsh: -------------------------------------------------------------------------------- 1 | uniform vec4 color; 2 | uniform sampler2D tex; 3 | 4 | varying vec2 frag_uv; 5 | 6 | void main(void) 7 | { 8 | gl_FragColor = color * texture2D(tex, frag_uv); 9 | } 10 | -------------------------------------------------------------------------------- /shader/fullbright_textured.vsh: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // No lighting at all, solid color, single texture 4 | // 5 | 6 | uniform vec4 color; 7 | uniform mat4 mat; 8 | attribute vec3 pos; 9 | attribute vec2 uv; 10 | 11 | varying vec2 frag_uv; 12 | 13 | void main(void) 14 | { 15 | // xform pos into screen space 16 | gl_Position = mat * vec4(pos, 1); 17 | frag_uv = uv; 18 | } 19 | -------------------------------------------------------------------------------- /shader/fullbright_textured_text.fsh: -------------------------------------------------------------------------------- 1 | uniform vec4 color; 2 | uniform sampler2D tex; 3 | uniform float lod_bias; 4 | varying vec2 frag_uv; 5 | 6 | void main(void) 7 | { 8 | // if tex is an alpha only texture, the color will be zero. 9 | // so lets just use the color directly 10 | gl_FragColor.rgb = color.rgb; 11 | gl_FragColor.a = color.a * texture2D(tex, frag_uv, lod_bias).a; 12 | } 13 | -------------------------------------------------------------------------------- /shader/fullbright_textured_vert_color.fsh: -------------------------------------------------------------------------------- 1 | uniform vec4 color; 2 | uniform sampler2D tex; 3 | 4 | varying vec2 frag_uv; 5 | varying vec4 frag_color; 6 | 7 | void main(void) 8 | { 9 | gl_FragColor = frag_color * texture2D(tex, frag_uv); 10 | } 11 | -------------------------------------------------------------------------------- /shader/fullbright_textured_vert_color.vsh: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // No lighting at all, vertex color, single texture 4 | // 5 | 6 | uniform mat4 mat; 7 | attribute vec3 pos; 8 | attribute vec2 uv; 9 | attribute vec4 color; 10 | 11 | varying vec2 frag_uv; 12 | varying vec4 frag_color; 13 | 14 | void main(void) 15 | { 16 | // xform pos into screen space 17 | gl_Position = mat * vec4(pos, 1); 18 | frag_uv = uv; 19 | frag_color = color; 20 | } 21 | -------------------------------------------------------------------------------- /shader/fullbright_textured_vert_color_text.fsh: -------------------------------------------------------------------------------- 1 | uniform vec4 color; 2 | uniform sampler2D tex; 3 | uniform float lod_bias; 4 | 5 | varying vec2 frag_uv; 6 | varying vec4 frag_color; 7 | 8 | void main(void) 9 | { 10 | // if tex is an alpha only texture, the color will be zero. 11 | // so lets just use the color directly 12 | gl_FragColor.rgb = frag_color.rgb; 13 | gl_FragColor.a = frag_color.a * texture2D(tex, frag_uv, lod_bias).a; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /shader/fullbright_vert_color.fsh: -------------------------------------------------------------------------------- 1 | varying vec4 frag_color; 2 | 3 | void main(void) 4 | { 5 | gl_FragColor = frag_color; 6 | } 7 | -------------------------------------------------------------------------------- /shader/fullbright_vert_color.vsh: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // No lighting at all, solid color 4 | // 5 | 6 | uniform mat4 mat; 7 | attribute vec3 pos; 8 | attribute vec4 color; 9 | 10 | varying vec4 frag_color; 11 | 12 | void main(void) 13 | { 14 | gl_Position = mat * vec4(pos, 1); 15 | frag_color = color; 16 | } 17 | -------------------------------------------------------------------------------- /shader/phong_textured.fsh: -------------------------------------------------------------------------------- 1 | uniform vec4 color; 2 | uniform sampler2D tex; 3 | 4 | #define MAX_LIGHTS 8 5 | uniform vec3 light_world_pos[MAX_LIGHTS]; 6 | uniform vec3 light_color[MAX_LIGHTS]; 7 | uniform float light_strength[MAX_LIGHTS]; 8 | uniform int num_lights; 9 | 10 | varying vec2 frag_uv; 11 | varying vec3 frag_world_pos; 12 | varying vec3 frag_world_normal; 13 | 14 | void main(void) 15 | { 16 | vec3 d; 17 | vec3 accum = vec3(0.1, 0.1, 0.1); 18 | float len, att, len_over_str, intensity; 19 | int i; 20 | for (i = 0; i < num_lights; i++) { 21 | d = light_world_pos[i] - frag_world_pos; 22 | len = length(d); 23 | len_over_str = len / light_strength[i]; 24 | att = min(1.0 / (len_over_str * len_over_str), 1.0); 25 | intensity = max(dot(normalize(d), frag_world_normal), 0.0) * att; 26 | accum += light_color[i] * intensity; 27 | } 28 | 29 | gl_FragColor = vec4(accum, 1) * color * texture2D(tex, frag_uv); 30 | } 31 | -------------------------------------------------------------------------------- /shader/phong_textured.vsh: -------------------------------------------------------------------------------- 1 | uniform vec4 color; 2 | uniform mat4 full_mat; 3 | uniform mat4 world_mat; 4 | uniform mat4 world_normal_mat; 5 | //uniform sampler2D tex; 6 | 7 | #define MAX_LIGHTS 8 8 | uniform vec3 light_world_pos[MAX_LIGHTS]; 9 | uniform vec3 light_color[MAX_LIGHTS]; 10 | uniform float light_strength[MAX_LIGHTS]; 11 | uniform int num_lights; 12 | 13 | attribute vec3 pos; 14 | attribute vec2 uv; 15 | attribute vec3 normal; 16 | 17 | varying vec2 frag_uv; 18 | varying vec3 frag_world_pos; 19 | varying vec3 frag_world_normal; 20 | 21 | void main(void) 22 | { 23 | gl_Position = full_mat * vec4(pos, 1); 24 | frag_uv = uv; 25 | frag_world_pos = vec3(world_mat * vec4(pos, 1)); 26 | frag_world_normal = vec3(world_normal_mat * vec4(normal, 1)); 27 | } 28 | -------------------------------------------------------------------------------- /vnc-helper: -------------------------------------------------------------------------------- 1 | # Helper to view rift screen using vnc 2 | # Thanks, John Tsiombikas 3 | 4 | #!/bin/sh 5 | 6 | x11vnc -display :0.1 -scale 0.5 -rotate +90 \ 7 | -localhost -nopw -norc -timeout 1 \ 8 | -q -viewonly -once -bg && \ 9 | vncviewer localhost --------------------------------------------------------------------------------