Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6679ff7b5 | ||
|
|
de80003103 | ||
|
|
2b0596c7f9 | ||
|
|
3d15a1d786 | ||
|
|
1fcb865202 | ||
|
|
1c56de5605 | ||
|
|
adbce71fb4 | ||
|
|
00e3ea8c08 | ||
|
|
36432c8616 | ||
|
|
03e0b9de04 | ||
|
|
4364163ceb | ||
|
|
ffd9ec653d | ||
|
|
ee3c3e4cb4 | ||
|
|
277c46908d | ||
|
|
8e27c80e1f | ||
|
|
afa767c3a8 | ||
|
|
67ec04fba3 | ||
|
|
f35bf7f1ba | ||
|
|
4fdb55e260 | ||
|
|
be06669e8f | ||
|
|
313253cd95 | ||
|
|
8c16be2ccd | ||
|
|
e1b276786b | ||
|
|
05f5a05c20 | ||
|
|
7e79aac275 | ||
|
|
0a21819e16 | ||
|
|
fe3e51a00d | ||
|
|
21874339f1 | ||
|
|
4f22fcdaac | ||
|
|
0424d665d5 | ||
|
|
0bbea6812e | ||
|
|
101a50b0ff | ||
|
|
a9f3a698d2 | ||
|
|
5117a2c17a | ||
|
|
ebce1d7c61 | ||
|
|
e4668c1aff | ||
|
|
2519c49c02 | ||
|
|
9e512ff58a | ||
|
|
ed50595aca | ||
|
|
9d62d087e0 | ||
|
|
6b933e33f5 | ||
|
|
a86931f401 | ||
|
|
fc4e1595e8 | ||
|
|
b36c3f3425 | ||
|
|
e427fd1233 | ||
|
|
dd3af45043 | ||
|
|
304d42d4c2 | ||
|
|
abca9280e7 |
@@ -3,12 +3,14 @@
|
|||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
from ctypes import addressof
|
from ctypes import addressof
|
||||||
|
from functools import partial
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
from .constants import GLfloat, GLint, GLuint, viewport_size
|
from .constants import GLfloat, GLint, GLuint, viewport_size
|
||||||
from .fast_data_types import GL_TRIANGLE_FAN, glMultiDrawArrays, glUniform3fv
|
from .fast_data_types import (
|
||||||
|
GL_STATIC_DRAW, GL_TRIANGLE_FAN, glMultiDrawArrays, glUniform3fv
|
||||||
|
)
|
||||||
from .shaders import ShaderProgram
|
from .shaders import ShaderProgram
|
||||||
from .utils import pt_to_px
|
from .utils import pt_to_px
|
||||||
|
|
||||||
@@ -50,10 +52,10 @@ void main() {
|
|||||||
final_color = vec4(color, 1);
|
final_color = vec4(color, 1);
|
||||||
}
|
}
|
||||||
''')
|
''')
|
||||||
self.add_vertex_array('rect')
|
self.vao_id = self.add_vertex_arrays(self.vertex_array('rect'))
|
||||||
|
|
||||||
def send_data(self, data):
|
def send_data(self, data):
|
||||||
self.send_vertex_data('rect', data)
|
self.send_vertex_data(self.vao_id, data, usage=GL_STATIC_DRAW)
|
||||||
|
|
||||||
def set_colors(self, color_buf):
|
def set_colors(self, color_buf):
|
||||||
glUniform3fv(self.uniform_location('colors'), 3, addressof(color_buf))
|
glUniform3fv(self.uniform_location('colors'), 3, addressof(color_buf))
|
||||||
@@ -149,7 +151,8 @@ class Borders:
|
|||||||
program.send_data(self.rects)
|
program.send_data(self.rects)
|
||||||
program.set_colors(self.color_buf)
|
program.set_colors(self.color_buf)
|
||||||
self.is_dirty = False
|
self.is_dirty = False
|
||||||
glMultiDrawArrays(
|
with program.bound_vertex_array(program.vao_id):
|
||||||
GL_TRIANGLE_FAN,
|
glMultiDrawArrays(
|
||||||
addressof(self.starts),
|
GL_TRIANGLE_FAN,
|
||||||
addressof(self.counts), self.num_of_rects)
|
addressof(self.starts),
|
||||||
|
addressof(self.counts), self.num_of_rects)
|
||||||
|
|||||||
110
kitty/boss.py
110
kitty/boss.py
@@ -2,38 +2,42 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import os
|
import inspect
|
||||||
import io
|
import io
|
||||||
|
import os
|
||||||
import select
|
import select
|
||||||
import signal
|
import signal
|
||||||
import struct
|
import struct
|
||||||
import inspect
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from gettext import gettext as _
|
||||||
|
from queue import Empty, Queue
|
||||||
from threading import Thread, current_thread
|
from threading import Thread, current_thread
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
from queue import Queue, Empty
|
|
||||||
from gettext import gettext as _
|
|
||||||
|
|
||||||
|
from .borders import BordersProgram
|
||||||
|
from .char_grid import load_shader_programs
|
||||||
from .config import MINIMUM_FONT_SIZE
|
from .config import MINIMUM_FONT_SIZE
|
||||||
from .constants import (
|
from .constants import (
|
||||||
viewport_size, set_boss, wakeup, cell_size, MODIFIER_KEYS,
|
MODIFIER_KEYS, cell_size, is_key_pressed, isosx, main_thread,
|
||||||
main_thread, mouse_button_pressed, mouse_cursor_pos
|
mouse_button_pressed, mouse_cursor_pos, set_boss, viewport_size, wakeup
|
||||||
)
|
)
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
glViewport, glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GLFW_PRESS,
|
GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GLFW_CURSOR, GLFW_CURSOR_HIDDEN,
|
||||||
GLFW_REPEAT, GLFW_MOUSE_BUTTON_1, glfw_post_empty_event,
|
GLFW_CURSOR_NORMAL, GLFW_MOUSE_BUTTON_1, GLFW_PRESS, GLFW_REPEAT,
|
||||||
GLFW_CURSOR_NORMAL, GLFW_CURSOR, GLFW_CURSOR_HIDDEN, drain_read
|
drain_read, glBlendFunc, glfw_post_empty_event, glViewport
|
||||||
)
|
)
|
||||||
from .fonts.render import set_font_family
|
from .fonts.render import set_font_family
|
||||||
from .borders import BordersProgram
|
from .keys import (
|
||||||
from .char_grid import cursor_shader, cell_shader
|
get_sent_data, get_shortcut, interpret_key_event, interpret_text_event
|
||||||
from .constants import is_key_pressed
|
)
|
||||||
from .keys import interpret_text_event, interpret_key_event, get_shortcut
|
|
||||||
from .session import create_session
|
from .session import create_session
|
||||||
from .shaders import Sprites, ShaderProgram
|
from .shaders import Sprites
|
||||||
from .tabs import TabManager, SpecialWindow
|
from .tabs import SpecialWindow, TabManager
|
||||||
from .timers import Timers
|
from .timers import Timers
|
||||||
from .utils import handle_unix_signals, safe_print, pipe2
|
from .utils import handle_unix_signals, pipe2, safe_print
|
||||||
|
|
||||||
|
if isosx:
|
||||||
|
from .fast_data_types import cocoa_update_title
|
||||||
|
|
||||||
|
|
||||||
def conditional_run(w, i):
|
def conditional_run(w, i):
|
||||||
@@ -59,6 +63,7 @@ def callback(func):
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.queue_action(conditional_run, w, i)
|
self.queue_action(conditional_run, w, i)
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
|
||||||
@@ -80,7 +85,9 @@ class Boss(Thread):
|
|||||||
self.screen_update_delay = opts.repaint_delay / 1000.0
|
self.screen_update_delay = opts.repaint_delay / 1000.0
|
||||||
self.signal_fd = handle_unix_signals()
|
self.signal_fd = handle_unix_signals()
|
||||||
self.read_wakeup_fd, self.write_wakeup_fd = pipe2()
|
self.read_wakeup_fd, self.write_wakeup_fd = pipe2()
|
||||||
self.read_dispatch_map = {self.signal_fd: self.signal_received, self.read_wakeup_fd: self.on_wakeup}
|
self.read_dispatch_map = {
|
||||||
|
self.signal_fd: self.signal_received,
|
||||||
|
self.read_wakeup_fd: self.on_wakeup}
|
||||||
self.all_writers = []
|
self.all_writers = []
|
||||||
self.timers = Timers()
|
self.timers = Timers()
|
||||||
self.ui_timers = Timers()
|
self.ui_timers = Timers()
|
||||||
@@ -100,8 +107,7 @@ class Boss(Thread):
|
|||||||
glfw_window.window_focus_callback = self.on_focus
|
glfw_window.window_focus_callback = self.on_focus
|
||||||
self.tab_manager = TabManager(opts, args, startup_session)
|
self.tab_manager = TabManager(opts, args, startup_session)
|
||||||
self.sprites = Sprites()
|
self.sprites = Sprites()
|
||||||
self.cell_program = ShaderProgram(*cell_shader)
|
self.cell_program, self.cursor_program = load_shader_programs()
|
||||||
self.cursor_program = ShaderProgram(*cursor_shader)
|
|
||||||
self.borders_program = BordersProgram()
|
self.borders_program = BordersProgram()
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||||
self.sprites.do_layout(cell_size.width, cell_size.height)
|
self.sprites.do_layout(cell_size.width, cell_size.height)
|
||||||
@@ -205,8 +211,10 @@ class Boss(Thread):
|
|||||||
def loop(self):
|
def loop(self):
|
||||||
while not self.shutting_down:
|
while not self.shutting_down:
|
||||||
all_readers = list(self.read_dispatch_map)
|
all_readers = list(self.read_dispatch_map)
|
||||||
all_writers = [w.child_fd for w in self.iterwindows() if w.write_buf]
|
all_writers = [
|
||||||
readers, writers, _ = select.select(all_readers, all_writers, [], self.timers.timeout())
|
w.child_fd for w in self.iterwindows() if w.write_buf]
|
||||||
|
readers, writers, _ = select.select(
|
||||||
|
all_readers, all_writers, [], self.timers.timeout())
|
||||||
for r in readers:
|
for r in readers:
|
||||||
self.read_dispatch_map[r]()
|
self.read_dispatch_map[r]()
|
||||||
for w in writers:
|
for w in writers:
|
||||||
@@ -214,7 +222,8 @@ class Boss(Thread):
|
|||||||
self.timers()
|
self.timers()
|
||||||
for w in self.iterwindows():
|
for w in self.iterwindows():
|
||||||
if w.screen.is_dirty():
|
if w.screen.is_dirty():
|
||||||
self.timers.add_if_missing(self.screen_update_delay, w.update_screen)
|
self.timers.add_if_missing(
|
||||||
|
self.screen_update_delay, w.update_screen)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def on_window_resize(self, window, w, h):
|
def on_window_resize(self, window, w, h):
|
||||||
@@ -231,10 +240,16 @@ class Boss(Thread):
|
|||||||
glfw_post_empty_event()
|
glfw_post_empty_event()
|
||||||
|
|
||||||
def increase_font_size(self):
|
def increase_font_size(self):
|
||||||
self.change_font_size(min(self.opts.font_size * 5, self.current_font_size + self.opts.font_size_delta))
|
self.change_font_size(
|
||||||
|
min(
|
||||||
|
self.opts.font_size * 5, self.current_font_size +
|
||||||
|
self.opts.font_size_delta))
|
||||||
|
|
||||||
def decrease_font_size(self):
|
def decrease_font_size(self):
|
||||||
self.change_font_size(max(MINIMUM_FONT_SIZE, self.current_font_size - self.opts.font_size_delta))
|
self.change_font_size(
|
||||||
|
max(
|
||||||
|
MINIMUM_FONT_SIZE, self.current_font_size -
|
||||||
|
self.opts.font_size_delta))
|
||||||
|
|
||||||
def restore_font_size(self):
|
def restore_font_size(self):
|
||||||
self.change_font_size(self.opts.font_size)
|
self.change_font_size(self.opts.font_size)
|
||||||
@@ -308,7 +323,9 @@ class Boss(Thread):
|
|||||||
return
|
return
|
||||||
if window.char_grid.scrolled_by and key not in MODIFIER_KEYS and action == GLFW_PRESS:
|
if window.char_grid.scrolled_by and key not in MODIFIER_KEYS and action == GLFW_PRESS:
|
||||||
window.scroll_end()
|
window.scroll_end()
|
||||||
data = interpret_key_event(key, scancode, mods, window, action)
|
data = get_sent_data(
|
||||||
|
self.opts.send_text_map, key, scancode, mods, window, action
|
||||||
|
) or interpret_key_event(key, scancode, mods, window, action)
|
||||||
if data:
|
if data:
|
||||||
window.write_to_child(data)
|
window.write_to_child(data)
|
||||||
|
|
||||||
@@ -326,7 +343,9 @@ class Boss(Thread):
|
|||||||
else:
|
else:
|
||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
if tab is not None:
|
if tab is not None:
|
||||||
tab.new_special_window(SpecialWindow(self.opts.scrollback_pager, data, _('History')))
|
tab.new_special_window(
|
||||||
|
SpecialWindow(
|
||||||
|
self.opts.scrollback_pager, data, _('History')))
|
||||||
|
|
||||||
def window_for_pos(self, x, y):
|
def window_for_pos(self, x, y):
|
||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
@@ -365,7 +384,8 @@ class Boss(Thread):
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
def on_mouse_move(self, window, xpos, ypos):
|
def on_mouse_move(self, window, xpos, ypos):
|
||||||
mouse_cursor_pos[:2] = xpos, ypos = int(xpos * viewport_size.x_ratio), int(ypos * viewport_size.y_ratio)
|
mouse_cursor_pos[:2] = xpos, ypos = int(
|
||||||
|
xpos * viewport_size.x_ratio), int(ypos * viewport_size.y_ratio)
|
||||||
self.show_mouse_cursor()
|
self.show_mouse_cursor()
|
||||||
w = self.window_for_pos(xpos, ypos)
|
w = self.window_for_pos(xpos, ypos)
|
||||||
if w is not None:
|
if w is not None:
|
||||||
@@ -387,7 +407,8 @@ class Boss(Thread):
|
|||||||
def show_mouse_cursor(self):
|
def show_mouse_cursor(self):
|
||||||
self.glfw_window.set_input_mode(GLFW_CURSOR, GLFW_CURSOR_NORMAL)
|
self.glfw_window.set_input_mode(GLFW_CURSOR, GLFW_CURSOR_NORMAL)
|
||||||
if self.opts.mouse_hide_wait > 0:
|
if self.opts.mouse_hide_wait > 0:
|
||||||
self.ui_timers.add(self.opts.mouse_hide_wait, self.hide_mouse_cursor)
|
self.ui_timers.add(
|
||||||
|
self.opts.mouse_hide_wait, self.hide_mouse_cursor)
|
||||||
|
|
||||||
def hide_mouse_cursor(self):
|
def hide_mouse_cursor(self):
|
||||||
self.glfw_window.set_input_mode(GLFW_CURSOR, GLFW_CURSOR_HIDDEN)
|
self.glfw_window.set_input_mode(GLFW_CURSOR, GLFW_CURSOR_HIDDEN)
|
||||||
@@ -399,12 +420,14 @@ class Boss(Thread):
|
|||||||
try:
|
try:
|
||||||
self.glfw_window.request_window_attention()
|
self.glfw_window.request_window_attention()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass # needs glfw 3.3
|
pass # needs glfw 3.3
|
||||||
|
|
||||||
def start_cursor_blink(self):
|
def start_cursor_blink(self):
|
||||||
self.cursor_blinking = True
|
self.cursor_blinking = True
|
||||||
if self.opts.cursor_stop_blinking_after > 0:
|
if self.opts.cursor_stop_blinking_after > 0:
|
||||||
self.ui_timers.add(self.opts.cursor_stop_blinking_after, self.stop_cursor_blinking)
|
self.ui_timers.add(
|
||||||
|
self.opts.cursor_stop_blinking_after,
|
||||||
|
self.stop_cursor_blinking)
|
||||||
|
|
||||||
def stop_cursor_blinking(self):
|
def stop_cursor_blinking(self):
|
||||||
self.cursor_blinking = False
|
self.cursor_blinking = False
|
||||||
@@ -420,15 +443,22 @@ class Boss(Thread):
|
|||||||
if tab.title != self.glfw_window_title:
|
if tab.title != self.glfw_window_title:
|
||||||
self.glfw_window_title = tab.title
|
self.glfw_window_title = tab.title
|
||||||
self.glfw_window.set_title(self.glfw_window_title)
|
self.glfw_window.set_title(self.glfw_window_title)
|
||||||
|
if isosx:
|
||||||
|
cocoa_update_title(self.glfw_window_title)
|
||||||
with self.sprites:
|
with self.sprites:
|
||||||
self.sprites.render_dirty_cells()
|
self.sprites.render_dirty_cells()
|
||||||
tab.render()
|
tab.render()
|
||||||
render_data = {window: window.char_grid.prepare_for_render(self.sprites) for window in tab.visible_windows() if not window.needs_layout}
|
render_data = {
|
||||||
|
window:
|
||||||
|
window.char_grid.prepare_for_render(self.cell_program)
|
||||||
|
for window in tab.visible_windows()
|
||||||
|
if not window.needs_layout}
|
||||||
with self.cell_program:
|
with self.cell_program:
|
||||||
self.tab_manager.render(self.cell_program, self.sprites)
|
self.tab_manager.render(self.cell_program, self.sprites)
|
||||||
for window, rd in render_data.items():
|
for window, rd in render_data.items():
|
||||||
if rd is not None:
|
if rd is not None:
|
||||||
window.render_cells(rd, self.cell_program, self.sprites)
|
window.render_cells(
|
||||||
|
rd, self.cell_program, self.sprites)
|
||||||
active = self.active_window
|
active = self.active_window
|
||||||
rd = render_data.get(active)
|
rd = render_data.get(active)
|
||||||
if rd is not None:
|
if rd is not None:
|
||||||
@@ -439,15 +469,16 @@ class Boss(Thread):
|
|||||||
d = int(self.opts.cursor_blink_interval * 1000)
|
d = int(self.opts.cursor_blink_interval * 1000)
|
||||||
n = t // d
|
n = t // d
|
||||||
draw_cursor = n % 2 == 0
|
draw_cursor = n % 2 == 0
|
||||||
self.ui_timers.add_if_missing(((n + 1) * d / 1000) - now, glfw_post_empty_event)
|
self.ui_timers.add_if_missing(
|
||||||
|
((n + 1) * d / 1000) - now, glfw_post_empty_event)
|
||||||
if draw_cursor:
|
if draw_cursor:
|
||||||
with self.cursor_program:
|
with self.cursor_program:
|
||||||
active.char_grid.render_cursor(rd, self.cursor_program, self.window_is_focused)
|
active.char_grid.render_cursor(
|
||||||
|
rd, self.cursor_program,
|
||||||
|
self.window_is_focused)
|
||||||
|
|
||||||
def gui_close_window(self, window):
|
def gui_close_window(self, window):
|
||||||
if window.char_grid.buffer_id is not None:
|
window.char_grid.destroy(self.cell_program)
|
||||||
self.sprites.destroy_sprite_map(window.char_grid.buffer_id)
|
|
||||||
window.char_grid.buffer_id = None
|
|
||||||
for tab in self.tab_manager:
|
for tab in self.tab_manager:
|
||||||
if window in tab:
|
if window in tab:
|
||||||
break
|
break
|
||||||
@@ -499,5 +530,8 @@ class Boss(Thread):
|
|||||||
self.queue_action(self.tab_manager.move_tab, -1)
|
self.queue_action(self.tab_manager.move_tab, -1)
|
||||||
|
|
||||||
def display_scrollback_in_new_tab(self, data):
|
def display_scrollback_in_new_tab(self, data):
|
||||||
self.tab_manager.new_tab(special_window=SpecialWindow(self.opts.scrollback_pager, data, _('History')))
|
self.tab_manager.new_tab(
|
||||||
|
special_window=SpecialWindow(
|
||||||
|
self.opts.scrollback_pager, data, _('History')))
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|||||||
26
kitty/cell_fragment.glsl
Normal file
26
kitty/cell_fragment.glsl
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
uniform sampler2DArray sprites;
|
||||||
|
in vec3 sprite_pos;
|
||||||
|
in vec3 underline_pos;
|
||||||
|
in vec3 strike_pos;
|
||||||
|
in vec3 foreground;
|
||||||
|
in vec3 background;
|
||||||
|
in vec3 decoration_fg;
|
||||||
|
out vec4 final_color;
|
||||||
|
|
||||||
|
vec3 blend(float alpha, vec3 over, vec3 under) {
|
||||||
|
return over + (1 - alpha) * under;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float text_alpha = texture(sprites, sprite_pos).r;
|
||||||
|
float underline_alpha = texture(sprites, underline_pos).r;
|
||||||
|
float strike_alpha = texture(sprites, strike_pos).r;
|
||||||
|
vec3 underline = underline_alpha * decoration_fg;
|
||||||
|
vec3 strike = strike_alpha * foreground;
|
||||||
|
vec3 fg = text_alpha * foreground;
|
||||||
|
vec3 decoration = blend(underline_alpha, underline, strike);
|
||||||
|
vec3 combined_fg = blend(text_alpha, fg, decoration);
|
||||||
|
float combined_alpha = max(max(underline_alpha, strike_alpha), text_alpha);
|
||||||
|
final_color = vec4(blend(combined_alpha, combined_fg, background), 1);
|
||||||
|
}
|
||||||
|
|
||||||
59
kitty/cell_vertex.glsl
Normal file
59
kitty/cell_vertex.glsl
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
uniform uvec2 dimensions; // xnum, ynum
|
||||||
|
uniform vec4 steps; // xstart, ystart, dx, dy
|
||||||
|
uniform vec2 sprite_layout; // dx, dy
|
||||||
|
uniform ivec2 color_indices; // which color to use as fg and which as bg
|
||||||
|
in uvec3 sprite_coords;
|
||||||
|
in uvec3 colors;
|
||||||
|
out vec3 sprite_pos;
|
||||||
|
out vec3 underline_pos;
|
||||||
|
out vec3 strike_pos;
|
||||||
|
out vec3 foreground;
|
||||||
|
out vec3 background;
|
||||||
|
out vec3 decoration_fg;
|
||||||
|
|
||||||
|
const uvec2 pos_map[] = uvec2[4](
|
||||||
|
uvec2(1, 0), // right, top
|
||||||
|
uvec2(1, 1), // right, bottom
|
||||||
|
uvec2(0, 1), // left, bottom
|
||||||
|
uvec2(0, 0) // left, top
|
||||||
|
);
|
||||||
|
|
||||||
|
const uint BYTE_MASK = uint(255);
|
||||||
|
const uint ZERO = uint(0);
|
||||||
|
const uint SMASK = uint(3);
|
||||||
|
|
||||||
|
vec3 to_color(uint c) {
|
||||||
|
uint r, g, b;
|
||||||
|
r = (c >> 16) & BYTE_MASK;
|
||||||
|
g = (c >> 8) & BYTE_MASK;
|
||||||
|
b = c & BYTE_MASK;
|
||||||
|
return vec3(float(r) / 255.0, float(g) / 255.0, float(b) / 255.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 to_sprite_pos(uvec2 pos, uint x, uint y, uint z) {
|
||||||
|
vec2 s_xpos = vec2(x, float(x) + 1.0) * sprite_layout.x;
|
||||||
|
vec2 s_ypos = vec2(y, float(y) + 1.0) * sprite_layout.y;
|
||||||
|
return vec3(s_xpos[pos.x], s_ypos[pos.y], z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint instance_id = uint(gl_InstanceID);
|
||||||
|
uint r = instance_id / dimensions[0];
|
||||||
|
uint c = instance_id - r * dimensions[0];
|
||||||
|
float left = steps[0] + c * steps[2];
|
||||||
|
float top = steps[1] - r * steps[3];
|
||||||
|
vec2 xpos = vec2(left, left + steps[2]);
|
||||||
|
vec2 ypos = vec2(top, top - steps[3]);
|
||||||
|
uvec2 pos = pos_map[gl_VertexID];
|
||||||
|
gl_Position = vec4(xpos[pos.x], ypos[pos.y], 0, 1);
|
||||||
|
|
||||||
|
sprite_pos = to_sprite_pos(pos, sprite_coords.x, sprite_coords.y, sprite_coords.z);
|
||||||
|
uint fg = colors[color_indices[0]];
|
||||||
|
uint bg = colors[color_indices[1]];
|
||||||
|
uint decoration = colors[2];
|
||||||
|
foreground = to_color(fg);
|
||||||
|
background = to_color(bg);
|
||||||
|
decoration_fg = to_color(decoration);
|
||||||
|
underline_pos = to_sprite_pos(pos, (decoration >> 24) & SMASK, ZERO, ZERO);
|
||||||
|
strike_pos = to_sprite_pos(pos, (decoration >> 26) & SMASK, ZERO, ZERO);
|
||||||
|
}
|
||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from enum import Enum
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from ctypes import addressof, memmove, sizeof
|
from ctypes import addressof, memmove, sizeof
|
||||||
|
from enum import Enum
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
from .config import build_ansi_color_table, defaults
|
from .config import build_ansi_color_table, defaults
|
||||||
@@ -15,11 +15,12 @@ from .constants import (
|
|||||||
)
|
)
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, DATA_CELL_SIZE, GL_BLEND,
|
CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, DATA_CELL_SIZE, GL_BLEND,
|
||||||
GL_LINE_LOOP, GL_TRIANGLE_FAN, ColorProfile, glDisable, glDrawArrays,
|
GL_LINE_LOOP, GL_TRIANGLE_FAN, GL_UNSIGNED_INT, ColorProfile, glDisable,
|
||||||
glDrawArraysInstanced, glEnable, glUniform1i, glUniform2f, glUniform2ui,
|
glDrawArrays, glDrawArraysInstanced, glEnable, glUniform1i, glUniform2f,
|
||||||
glUniform4f
|
glUniform2i, glUniform2ui, glUniform4f
|
||||||
)
|
)
|
||||||
from .rgb import to_color
|
from .rgb import to_color
|
||||||
|
from .shaders import ShaderProgram, load_shaders
|
||||||
from .utils import (
|
from .utils import (
|
||||||
color_as_int, color_from_int, get_logical_dpi, open_url, safe_print,
|
color_as_int, color_from_int, get_logical_dpi, open_url, safe_print,
|
||||||
set_primary_selection
|
set_primary_selection
|
||||||
@@ -32,133 +33,22 @@ class DynamicColor(Enum):
|
|||||||
default_fg, default_bg, cursor_color, highlight_fg, highlight_bg = range(1, 6)
|
default_fg, default_bg, cursor_color, highlight_fg, highlight_bg = range(1, 6)
|
||||||
|
|
||||||
|
|
||||||
if DATA_CELL_SIZE % 3:
|
class CellProgram(ShaderProgram):
|
||||||
raise ValueError('Incorrect data cell size, must be a multiple of 3')
|
|
||||||
|
def create_sprite_map(self):
|
||||||
|
stride = DATA_CELL_SIZE * sizeof(GLuint)
|
||||||
|
size = DATA_CELL_SIZE // 2
|
||||||
|
return self.add_vertex_arrays(
|
||||||
|
self.vertex_array('sprite_coords', size=size, dtype=GL_UNSIGNED_INT, stride=stride, divisor=1),
|
||||||
|
self.vertex_array('colors', size=size, dtype=GL_UNSIGNED_INT, stride=stride, offset=stride // 2, divisor=1),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# cell shader {{{
|
def load_shader_programs():
|
||||||
|
cell = CellProgram(*load_shaders('cell'))
|
||||||
cell_shader = (
|
cursor = ShaderProgram(*load_shaders('cursor'))
|
||||||
'''\
|
cursor.vao_id = cursor.add_vertex_arrays()
|
||||||
uniform uvec2 dimensions; // xnum, ynum
|
return cell, cursor
|
||||||
uniform vec4 steps; // xstart, ystart, dx, dy
|
|
||||||
uniform vec2 sprite_layout; // dx, dy
|
|
||||||
uniform usamplerBuffer sprite_map; // gl_InstanceID -> x, y, z
|
|
||||||
uniform uvec2 color_indices; // which color to use as fg and which as bg
|
|
||||||
out vec3 sprite_pos;
|
|
||||||
out vec3 underline_pos;
|
|
||||||
out vec3 strike_pos;
|
|
||||||
out vec3 foreground;
|
|
||||||
out vec3 background;
|
|
||||||
out vec3 decoration_fg;
|
|
||||||
|
|
||||||
const uvec2 pos_map[] = uvec2[4](
|
|
||||||
uvec2(1, 0), // right, top
|
|
||||||
uvec2(1, 1), // right, bottom
|
|
||||||
uvec2(0, 1), // left, bottom
|
|
||||||
uvec2(0, 0) // left, top
|
|
||||||
);
|
|
||||||
|
|
||||||
const uint BYTE_MASK = uint(255);
|
|
||||||
const uint ZERO = uint(0);
|
|
||||||
const uint SMASK = uint(3);
|
|
||||||
|
|
||||||
vec3 to_color(uint c) {
|
|
||||||
uint r, g, b;
|
|
||||||
r = (c >> 16) & BYTE_MASK;
|
|
||||||
g = (c >> 8) & BYTE_MASK;
|
|
||||||
b = c & BYTE_MASK;
|
|
||||||
return vec3(float(r) / 255.0, float(g) / 255.0, float(b) / 255.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 to_sprite_pos(uvec2 pos, uint x, uint y, uint z) {
|
|
||||||
vec2 s_xpos = vec2(x, float(x) + 1.0) * sprite_layout[0];
|
|
||||||
vec2 s_ypos = vec2(y, float(y) + 1.0) * sprite_layout[1];
|
|
||||||
return vec3(s_xpos[pos[0]], s_ypos[pos[1]], z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
uint instance_id = uint(gl_InstanceID);
|
|
||||||
uint r = instance_id / dimensions[0];
|
|
||||||
uint c = instance_id - r * dimensions[0];
|
|
||||||
float left = steps[0] + c * steps[2];
|
|
||||||
float top = steps[1] - r * steps[3];
|
|
||||||
vec2 xpos = vec2(left, left + steps[2]);
|
|
||||||
vec2 ypos = vec2(top, top - steps[3]);
|
|
||||||
uvec2 pos = pos_map[gl_VertexID];
|
|
||||||
gl_Position = vec4(xpos[pos[0]], ypos[pos[1]], 0, 1);
|
|
||||||
|
|
||||||
int sprite_id = gl_InstanceID * STRIDE;
|
|
||||||
uvec4 spos = texelFetch(sprite_map, sprite_id);
|
|
||||||
uvec4 colors = texelFetch(sprite_map, sprite_id + 1);
|
|
||||||
sprite_pos = to_sprite_pos(pos, spos[0], spos[1], spos[2]);
|
|
||||||
foreground = to_color(colors[color_indices[0]]);
|
|
||||||
background = to_color(colors[color_indices[1]]);
|
|
||||||
uint decoration = colors[2];
|
|
||||||
decoration_fg = to_color(decoration);
|
|
||||||
underline_pos = to_sprite_pos(pos, (decoration >> 24) & SMASK, ZERO, ZERO);
|
|
||||||
strike_pos = to_sprite_pos(pos, (decoration >> 26) & SMASK, ZERO, ZERO);
|
|
||||||
}
|
|
||||||
'''.replace('STRIDE', str(DATA_CELL_SIZE // 3)),
|
|
||||||
|
|
||||||
'''\
|
|
||||||
uniform sampler2DArray sprites;
|
|
||||||
in vec3 sprite_pos;
|
|
||||||
in vec3 underline_pos;
|
|
||||||
in vec3 strike_pos;
|
|
||||||
in vec3 foreground;
|
|
||||||
in vec3 background;
|
|
||||||
in vec3 decoration_fg;
|
|
||||||
out vec4 final_color;
|
|
||||||
|
|
||||||
vec3 blend(float alpha, vec3 over, vec3 under) {
|
|
||||||
return over + (1 - alpha) * under;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
float text_alpha = texture(sprites, sprite_pos).r;
|
|
||||||
float underline_alpha = texture(sprites, underline_pos).r;
|
|
||||||
float strike_alpha = texture(sprites, strike_pos).r;
|
|
||||||
vec3 underline = underline_alpha * decoration_fg;
|
|
||||||
vec3 strike = strike_alpha * foreground;
|
|
||||||
vec3 fg = text_alpha * foreground;
|
|
||||||
vec3 decoration = blend(underline_alpha, underline, strike);
|
|
||||||
vec3 combined_fg = blend(text_alpha, fg, decoration);
|
|
||||||
float combined_alpha = max(max(underline_alpha, strike_alpha), text_alpha);
|
|
||||||
final_color = vec4(blend(combined_alpha, combined_fg, background), 1);
|
|
||||||
}
|
|
||||||
''')
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# cursor shader {{{
|
|
||||||
|
|
||||||
cursor_shader = (
|
|
||||||
'''\
|
|
||||||
uniform vec2 xpos;
|
|
||||||
uniform vec2 ypos;
|
|
||||||
|
|
||||||
const uvec2 pos_map[] = uvec2[4](
|
|
||||||
uvec2(1, 0), // right, top
|
|
||||||
uvec2(1, 1), // right, bottom
|
|
||||||
uvec2(0, 1), // left, bottom
|
|
||||||
uvec2(0, 0) // left, top
|
|
||||||
);
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
uvec2 pos = pos_map[gl_VertexID];
|
|
||||||
gl_Position = vec4(xpos[pos[0]], ypos[pos[1]], 0, 1);
|
|
||||||
}
|
|
||||||
''',
|
|
||||||
|
|
||||||
'''\
|
|
||||||
uniform vec4 color;
|
|
||||||
out vec4 final_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
final_color = color;
|
|
||||||
}
|
|
||||||
''')
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
class Selection: # {{{
|
class Selection: # {{{
|
||||||
@@ -230,16 +120,15 @@ def calculate_gl_geometry(window_geometry):
|
|||||||
return ScreenGeometry(xstart, ystart, window_geometry.xnum, window_geometry.ynum, dx, dy)
|
return ScreenGeometry(xstart, ystart, window_geometry.xnum, window_geometry.ynum, dx, dy)
|
||||||
|
|
||||||
|
|
||||||
def render_cells(buffer_id, sg, cell_program, sprites, invert_colors=False):
|
def render_cells(vao_id, sg, cell_program, sprites, invert_colors=False):
|
||||||
sprites.bind_sprite_map(buffer_id)
|
|
||||||
ul = cell_program.uniform_location
|
ul = cell_program.uniform_location
|
||||||
glUniform2ui(ul('dimensions'), sg.xnum, sg.ynum)
|
glUniform2ui(ul('dimensions'), sg.xnum, sg.ynum)
|
||||||
glUniform2ui(ul('color_indices'), 1 if invert_colors else 0, 0 if invert_colors else 1)
|
glUniform2i(ul('color_indices'), 1 if invert_colors else 0, 0 if invert_colors else 1)
|
||||||
glUniform4f(ul('steps'), sg.xstart, sg.ystart, sg.dx, sg.dy)
|
glUniform4f(ul('steps'), sg.xstart, sg.ystart, sg.dx, sg.dy)
|
||||||
glUniform1i(ul('sprites'), sprites.sampler_num)
|
glUniform1i(ul('sprites'), sprites.sampler_num)
|
||||||
glUniform1i(ul('sprite_map'), sprites.buffer_sampler_num)
|
|
||||||
glUniform2f(ul('sprite_layout'), *(sprites.layout))
|
glUniform2f(ul('sprite_layout'), *(sprites.layout))
|
||||||
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, sg.xnum * sg.ynum)
|
with cell_program.bound_vertex_array(vao_id):
|
||||||
|
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, sg.xnum * sg.ynum)
|
||||||
|
|
||||||
|
|
||||||
class CharGrid:
|
class CharGrid:
|
||||||
@@ -248,7 +137,7 @@ class CharGrid:
|
|||||||
|
|
||||||
def __init__(self, screen, opts):
|
def __init__(self, screen, opts):
|
||||||
self.buffer_lock = Lock()
|
self.buffer_lock = Lock()
|
||||||
self.buffer_id = None
|
self.vao_id = None
|
||||||
self.current_selection = Selection()
|
self.current_selection = Selection()
|
||||||
self.last_rendered_selection = self.current_selection.limits(0, screen.lines, screen.columns)
|
self.last_rendered_selection = self.current_selection.limits(0, screen.lines, screen.columns)
|
||||||
self.render_buf_is_dirty = True
|
self.render_buf_is_dirty = True
|
||||||
@@ -276,6 +165,11 @@ class CharGrid:
|
|||||||
safe_print('Invalid characters in select_by_word_characters, ignoring', file=sys.stderr)
|
safe_print('Invalid characters in select_by_word_characters, ignoring', file=sys.stderr)
|
||||||
self.word_pat = re.compile(r'[\w{}]'.format(escape(defaults.select_by_word_characters)), re.UNICODE)
|
self.word_pat = re.compile(r'[\w{}]'.format(escape(defaults.select_by_word_characters)), re.UNICODE)
|
||||||
|
|
||||||
|
def destroy(self, cell_program):
|
||||||
|
if self.vao_id is not None:
|
||||||
|
cell_program.remove_vertex_array(self.vao_id)
|
||||||
|
self.vao_id = None
|
||||||
|
|
||||||
def update_position(self, window_geometry):
|
def update_position(self, window_geometry):
|
||||||
self.screen_geometry = calculate_gl_geometry(window_geometry)
|
self.screen_geometry = calculate_gl_geometry(window_geometry)
|
||||||
|
|
||||||
@@ -297,7 +191,7 @@ class CharGrid:
|
|||||||
if raw is None:
|
if raw is None:
|
||||||
return 0
|
return 0
|
||||||
val = to_color(raw)
|
val = to_color(raw)
|
||||||
return None if val is None else (color_as_int(val) << 8) | 1
|
return None if val is None else (color_as_int(val) << 8) | 2
|
||||||
|
|
||||||
for which, val in changes.items():
|
for which, val in changes.items():
|
||||||
val = item(val)
|
val = item(val)
|
||||||
@@ -322,10 +216,9 @@ class CharGrid:
|
|||||||
sprites = get_boss().sprites
|
sprites = get_boss().sprites
|
||||||
is_dirty = self.screen.is_dirty()
|
is_dirty = self.screen.is_dirty()
|
||||||
with sprites.lock:
|
with sprites.lock:
|
||||||
fg = self.screen.default_fg
|
fg, bg = self.screen.default_fg, self.screen.default_bg
|
||||||
fg = fg >> 8 if fg & 1 else self.default_fg
|
fg = fg >> 8 if fg & 2 else self.default_fg
|
||||||
bg = self.screen.default_bg
|
bg = bg >> 8 if bg & 2 else self.default_bg
|
||||||
bg = bg >> 8 if bg & 1 else self.default_bg
|
|
||||||
cursor_changed, history_line_added_count = self.screen.update_cell_data(
|
cursor_changed, history_line_added_count = self.screen.update_cell_data(
|
||||||
sprites.backend, self.color_profile, addressof(self.main_sprite_map), fg, bg, force_full_refresh)
|
sprites.backend, self.color_profile, addressof(self.main_sprite_map), fg, bg, force_full_refresh)
|
||||||
if self.scrolled_by:
|
if self.scrolled_by:
|
||||||
@@ -461,13 +354,13 @@ class CharGrid:
|
|||||||
s = sel or self.current_selection
|
s = sel or self.current_selection
|
||||||
return s.text(self.screen.linebuf, self.screen.historybuf)
|
return s.text(self.screen.linebuf, self.screen.historybuf)
|
||||||
|
|
||||||
def prepare_for_render(self, sprites):
|
def prepare_for_render(self, cell_program):
|
||||||
with self.buffer_lock:
|
with self.buffer_lock:
|
||||||
sg = self.render_data
|
sg = self.render_data
|
||||||
if sg is None:
|
if sg is None:
|
||||||
return
|
return
|
||||||
if self.buffer_id is None:
|
if self.vao_id is None:
|
||||||
self.buffer_id = sprites.add_sprite_map()
|
self.vao_id = cell_program.create_sprite_map()
|
||||||
buf = self.render_buf
|
buf = self.render_buf
|
||||||
start, end = sel = self.current_selection.limits(self.scrolled_by, self.screen.lines, self.screen.columns)
|
start, end = sel = self.current_selection.limits(self.scrolled_by, self.screen.lines, self.screen.columns)
|
||||||
if start != end:
|
if start != end:
|
||||||
@@ -475,18 +368,18 @@ class CharGrid:
|
|||||||
if self.render_buf_is_dirty or sel != self.last_rendered_selection:
|
if self.render_buf_is_dirty or sel != self.last_rendered_selection:
|
||||||
memmove(buf, self.render_buf, sizeof(type(buf)))
|
memmove(buf, self.render_buf, sizeof(type(buf)))
|
||||||
fg = self.screen.highlight_fg
|
fg = self.screen.highlight_fg
|
||||||
fg = fg >> 8 if fg & 1 else self.highlight_fg
|
fg = fg >> 8 if fg & 2 else self.highlight_fg
|
||||||
bg = self.screen.highlight_bg
|
bg = self.screen.highlight_bg
|
||||||
bg = bg >> 8 if bg & 1 else self.highlight_bg
|
bg = bg >> 8 if bg & 2 else self.highlight_bg
|
||||||
self.screen.apply_selection(addressof(buf), start[0], start[1], end[0], end[1], fg, bg)
|
self.screen.apply_selection(addressof(buf), start[0], start[1], end[0], end[1], fg, bg)
|
||||||
if self.render_buf_is_dirty or self.last_rendered_selection != sel:
|
if self.render_buf_is_dirty or self.last_rendered_selection != sel:
|
||||||
sprites.set_sprite_map(self.buffer_id, buf)
|
cell_program.send_vertex_data(self.vao_id, buf)
|
||||||
self.render_buf_is_dirty = False
|
self.render_buf_is_dirty = False
|
||||||
self.last_rendered_selection = sel
|
self.last_rendered_selection = sel
|
||||||
return sg
|
return sg
|
||||||
|
|
||||||
def render_cells(self, sg, cell_program, sprites, invert_colors=False):
|
def render_cells(self, sg, cell_program, sprites, invert_colors=False):
|
||||||
render_cells(self.buffer_id, sg, cell_program, sprites, invert_colors=invert_colors)
|
render_cells(self.vao_id, sg, cell_program, sprites, invert_colors=invert_colors)
|
||||||
|
|
||||||
def render_cursor(self, sg, cursor_program, is_focused):
|
def render_cursor(self, sg, cursor_program, is_focused):
|
||||||
cursor = self.current_cursor
|
cursor = self.current_cursor
|
||||||
@@ -503,7 +396,7 @@ class CharGrid:
|
|||||||
left = sg.xstart + cursor.x * sg.dx
|
left = sg.xstart + cursor.x * sg.dx
|
||||||
top = sg.ystart - cursor.y * sg.dy
|
top = sg.ystart - cursor.y * sg.dy
|
||||||
cc = self.screen.cursor_color
|
cc = self.screen.cursor_color
|
||||||
col = color_from_int(cc >> 8) if cc & 1 else self.opts.cursor
|
col = color_from_int(cc >> 8) if cc & 2 else self.opts.cursor
|
||||||
shape = cursor.shape or self.default_cursor.shape
|
shape = cursor.shape or self.default_cursor.shape
|
||||||
alpha = self.opts.cursor_opacity
|
alpha = self.opts.cursor_opacity
|
||||||
if alpha < 1.0 and shape == CURSOR_BLOCK:
|
if alpha < 1.0 and shape == CURSOR_BLOCK:
|
||||||
@@ -516,5 +409,6 @@ class CharGrid:
|
|||||||
glUniform4f(ul('color'), col[0] / 255.0, col[1] / 255.0, col[2] / 255.0, alpha)
|
glUniform4f(ul('color'), col[0] / 255.0, col[1] / 255.0, col[2] / 255.0, alpha)
|
||||||
glUniform2f(ul('xpos'), left, right)
|
glUniform2f(ul('xpos'), left, right)
|
||||||
glUniform2f(ul('ypos'), top, bottom)
|
glUniform2f(ul('ypos'), top, bottom)
|
||||||
glDrawArrays(GL_TRIANGLE_FAN if is_focused else GL_LINE_LOOP, 0, 4)
|
with cursor_program.bound_vertex_array(cursor_program.vao_id):
|
||||||
|
glDrawArrays(GL_TRIANGLE_FAN if is_focused else GL_LINE_LOOP, 0, 4)
|
||||||
glDisable(GL_BLEND)
|
glDisable(GL_BLEND)
|
||||||
|
|||||||
@@ -125,14 +125,14 @@ def write_osc(code, string=''):
|
|||||||
write(OSC + str(code) + string + '\x07')
|
write(OSC + str(code) + string + '\x07')
|
||||||
|
|
||||||
|
|
||||||
set_dynamic_color = write_osc
|
set_dynamic_color = set_color_table_color = write_osc
|
||||||
|
|
||||||
|
|
||||||
def replay(raw):
|
def replay(raw):
|
||||||
for line in raw.splitlines():
|
for line in raw.splitlines():
|
||||||
if line.strip():
|
if line.strip():
|
||||||
cmd, rest = line.partition(' ')[::2]
|
cmd, rest = line.partition(' ')[::2]
|
||||||
if cmd in {'draw', 'set_title', 'set_icon'}:
|
if cmd in {'draw', 'set_title', 'set_icon', 'set_dynamic_color', 'set_color_table_color'}:
|
||||||
globals()[cmd](rest)
|
globals()[cmd](rest)
|
||||||
else:
|
else:
|
||||||
rest = map(int, rest.split()) if rest else ()
|
rest = map(int, rest.split()) if rest else ()
|
||||||
|
|||||||
@@ -9,17 +9,150 @@
|
|||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
#include <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
#ifndef NSWindowStyleMaskTitled
|
#include <AvailabilityMacros.h>
|
||||||
#define NSWindowStyleMaskTitled NSTitledWindowMask
|
// Needed for _NSGetProgname
|
||||||
|
#include <crt_externs.h>
|
||||||
|
|
||||||
|
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101200)
|
||||||
|
#define NSWindowStyleMaskResizable NSResizableWindowMask
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@interface MenuDispatcher : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MenuDispatcher
|
||||||
|
@end
|
||||||
|
|
||||||
|
static NSObject* menu_dispatcher = NULL;
|
||||||
|
static NSMenuItem* title_menu = NULL;
|
||||||
|
|
||||||
|
static NSString*
|
||||||
|
find_app_name(void) {
|
||||||
|
size_t i;
|
||||||
|
NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
|
||||||
|
|
||||||
|
// Keys to search for as potential application names
|
||||||
|
NSString* name_keys[] =
|
||||||
|
{
|
||||||
|
@"CFBundleDisplayName",
|
||||||
|
@"CFBundleName",
|
||||||
|
@"CFBundleExecutable",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(name_keys) / sizeof(name_keys[0]); i++)
|
||||||
|
{
|
||||||
|
id name = [infoDictionary objectForKey:name_keys[i]];
|
||||||
|
if (name &&
|
||||||
|
[name isKindOfClass:[NSString class]] &&
|
||||||
|
![name isEqualToString:@""])
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char** progname = _NSGetProgname();
|
||||||
|
if (progname && *progname)
|
||||||
|
return [NSString stringWithUTF8String:*progname];
|
||||||
|
|
||||||
|
// Really shouldn't get here
|
||||||
|
return @"kitty";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
cocoa_hide_titlebar(PyObject UNUSED *self, PyObject *window_id) {
|
cocoa_create_global_menu(PyObject UNUSED *_self) {
|
||||||
|
if (menu_dispatcher != NULL) { Py_RETURN_NONE; }
|
||||||
|
NSString* app_name = find_app_name();
|
||||||
|
menu_dispatcher = [[MenuDispatcher alloc] init];
|
||||||
|
NSMenu* bar = [[NSMenu alloc] init];
|
||||||
|
[NSApp setMainMenu:bar];
|
||||||
|
|
||||||
|
NSMenuItem* appMenuItem =
|
||||||
|
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||||
|
NSMenu* appMenu = [[NSMenu alloc] init];
|
||||||
|
[appMenuItem setSubmenu:appMenu];
|
||||||
|
|
||||||
|
[appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", app_name]
|
||||||
|
action:@selector(orderFrontStandardAboutPanel:)
|
||||||
|
keyEquivalent:@""];
|
||||||
|
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", app_name]
|
||||||
|
action:@selector(hide:)
|
||||||
|
keyEquivalent:@"h"];
|
||||||
|
[[appMenu addItemWithTitle:@"Hide Others"
|
||||||
|
action:@selector(hideOtherApplications:)
|
||||||
|
keyEquivalent:@"h"]
|
||||||
|
setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask];
|
||||||
|
[appMenu addItemWithTitle:@"Show All"
|
||||||
|
action:@selector(unhideAllApplications:)
|
||||||
|
keyEquivalent:@""];
|
||||||
|
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
|
||||||
|
NSMenu* servicesMenu = [[NSMenu alloc] init];
|
||||||
|
[NSApp setServicesMenu:servicesMenu];
|
||||||
|
[[appMenu addItemWithTitle:@"Services"
|
||||||
|
action:NULL
|
||||||
|
keyEquivalent:@""] setSubmenu:servicesMenu];
|
||||||
|
[servicesMenu release];
|
||||||
|
|
||||||
|
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
|
||||||
|
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", app_name]
|
||||||
|
action:@selector(terminate:)
|
||||||
|
keyEquivalent:@"q"];
|
||||||
|
[appMenu release];
|
||||||
|
|
||||||
|
NSMenuItem* windowMenuItem =
|
||||||
|
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||||
|
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||||
|
[NSApp setWindowsMenu:windowMenu];
|
||||||
|
[windowMenuItem setSubmenu:windowMenu];
|
||||||
|
|
||||||
|
[windowMenu addItemWithTitle:@"Minimize"
|
||||||
|
action:@selector(performMiniaturize:)
|
||||||
|
keyEquivalent:@"m"];
|
||||||
|
[windowMenu addItemWithTitle:@"Zoom"
|
||||||
|
action:@selector(performZoom:)
|
||||||
|
keyEquivalent:@""];
|
||||||
|
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
[windowMenu addItemWithTitle:@"Bring All to Front"
|
||||||
|
action:@selector(arrangeInFront:)
|
||||||
|
keyEquivalent:@""];
|
||||||
|
|
||||||
|
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
[[windowMenu addItemWithTitle:@"Enter Full Screen"
|
||||||
|
action:@selector(toggleFullScreen:)
|
||||||
|
keyEquivalent:@"f"]
|
||||||
|
setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask];
|
||||||
|
[windowMenu release];
|
||||||
|
|
||||||
|
|
||||||
|
[bar release];
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject*
|
||||||
|
cocoa_update_title(PyObject UNUSED *self, PyObject *pytitle) {
|
||||||
|
NSString *title = [[NSString alloc] initWithUTF8String:PyUnicode_AsUTF8(pytitle)];
|
||||||
|
NSMenu *bar = [NSApp mainMenu];
|
||||||
|
if (title_menu != NULL) {
|
||||||
|
[bar removeItem:title_menu];
|
||||||
|
}
|
||||||
|
title_menu = [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||||
|
NSMenu *m = [[NSMenu alloc] initWithTitle:[NSString stringWithFormat:@" :: %@", title]];
|
||||||
|
[title_menu setSubmenu:m];
|
||||||
|
[m release];
|
||||||
|
[title release];
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject*
|
||||||
|
cocoa_make_window_resizable(PyObject UNUSED *self, PyObject *window_id) {
|
||||||
NSWindow *window = (NSWindow*)PyLong_AsVoidPtr(window_id);
|
NSWindow *window = (NSWindow*)PyLong_AsVoidPtr(window_id);
|
||||||
|
|
||||||
@try {
|
@try {
|
||||||
[window setStyleMask:
|
[window setStyleMask:
|
||||||
[window styleMask] & ~NSWindowStyleMaskTitled];
|
[window styleMask] | NSWindowStyleMaskResizable];
|
||||||
} @catch (NSException *e) {
|
} @catch (NSException *e) {
|
||||||
return PyErr_Format(PyExc_ValueError, "Failed to set style mask: %s: %s", [[e name] UTF8String], [[e reason] UTF8String]);
|
return PyErr_Format(PyExc_ValueError, "Failed to set style mask: %s: %s", [[e name] UTF8String], [[e reason] UTF8String]);
|
||||||
}
|
}
|
||||||
|
|||||||
117
kitty/config.py
117
kitty/config.py
@@ -2,6 +2,7 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import ast
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -84,16 +85,22 @@ named_keys = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def parse_key(val, keymap):
|
def parse_shortcut(sc):
|
||||||
sc, action = val.partition(' ')[::2]
|
|
||||||
action = action.strip()
|
|
||||||
sc = sc.strip()
|
|
||||||
if not sc or not action:
|
|
||||||
return
|
|
||||||
parts = sc.split('+')
|
parts = sc.split('+')
|
||||||
mods = parse_mods(parts[:-1])
|
mods = parse_mods(parts[:-1])
|
||||||
key = parts[-1].upper()
|
key = parts[-1].upper()
|
||||||
key = getattr(defines, 'GLFW_KEY_' + named_keys.get(key, key), None)
|
key = getattr(defines, 'GLFW_KEY_' + named_keys.get(key, key), None)
|
||||||
|
if key is not None:
|
||||||
|
return mods, key
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_key(val, keymap):
|
||||||
|
sc, action = val.partition(' ')[::2]
|
||||||
|
sc, action = sc.strip(), action.strip()
|
||||||
|
if not sc or not action:
|
||||||
|
return
|
||||||
|
mods, key = parse_shortcut(sc)
|
||||||
if key is None:
|
if key is None:
|
||||||
safe_print(
|
safe_print(
|
||||||
'Shortcut: {} has an unknown key, ignoring'.format(val),
|
'Shortcut: {} has an unknown key, ignoring'.format(val),
|
||||||
@@ -137,6 +144,39 @@ def parse_symbol_map(val):
|
|||||||
return symbol_map
|
return symbol_map
|
||||||
|
|
||||||
|
|
||||||
|
def parse_send_text(val):
|
||||||
|
parts = val.split(' ')
|
||||||
|
|
||||||
|
def abort(msg):
|
||||||
|
safe_print(
|
||||||
|
'Send text: {} is invalid ({}), ignoring'.format(val, msg), file=sys.stderr
|
||||||
|
)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if len(parts) < 3:
|
||||||
|
return abort('Incomplete')
|
||||||
|
|
||||||
|
text = ' '.join(parts[2:])
|
||||||
|
mode, sc = parts[:2]
|
||||||
|
mods, key = parse_shortcut(sc.strip())
|
||||||
|
if key is None:
|
||||||
|
return abort('Invalid shortcut')
|
||||||
|
text = ast.literal_eval("'''" + text + "'''").encode('utf-8')
|
||||||
|
if not text:
|
||||||
|
return abort('Empty text')
|
||||||
|
|
||||||
|
if mode in ('all', '*'):
|
||||||
|
modes = parse_send_text.all_modes
|
||||||
|
else:
|
||||||
|
modes = frozenset(mode.split(',')).intersection(parse_send_text.all_modes)
|
||||||
|
if not modes:
|
||||||
|
return abort('Invalid keyboard modes')
|
||||||
|
return {mode: {(mods, key): text} for mode in modes}
|
||||||
|
|
||||||
|
|
||||||
|
parse_send_text.all_modes = frozenset({'normal', 'application', 'kitty'})
|
||||||
|
|
||||||
|
|
||||||
def to_open_url_modifiers(val):
|
def to_open_url_modifiers(val):
|
||||||
return parse_mods(val.split('+'))
|
return parse_mods(val.split('+'))
|
||||||
|
|
||||||
@@ -198,8 +238,10 @@ for a in ('active', 'inactive'):
|
|||||||
type_map['%s_tab_%s' % (a, b)] = lambda x: to_color(x, validate=True)
|
type_map['%s_tab_%s' % (a, b)] = lambda x: to_color(x, validate=True)
|
||||||
|
|
||||||
|
|
||||||
def parse_config(lines):
|
def parse_config(lines, check_keys=True):
|
||||||
ans = {'keymap': {}, 'symbol_map': {}}
|
ans = {'keymap': {}, 'symbol_map': {}, 'send_text_map': {'kitty': {}, 'normal': {}, 'application': {}}}
|
||||||
|
if check_keys:
|
||||||
|
all_keys = defaults._asdict()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line or line.startswith('#'):
|
if not line or line.startswith('#'):
|
||||||
@@ -213,6 +255,15 @@ def parse_config(lines):
|
|||||||
if key == 'symbol_map':
|
if key == 'symbol_map':
|
||||||
ans['symbol_map'].update(parse_symbol_map(val))
|
ans['symbol_map'].update(parse_symbol_map(val))
|
||||||
continue
|
continue
|
||||||
|
if key == 'send_text':
|
||||||
|
stvals = parse_send_text(val)
|
||||||
|
for k, v in ans['send_text_map'].items():
|
||||||
|
v.update(stvals.get(k, {}))
|
||||||
|
continue
|
||||||
|
if check_keys:
|
||||||
|
if key not in all_keys:
|
||||||
|
safe_print('Ignoring unknown config key: {}'.format(key), file=sys.stderr)
|
||||||
|
continue
|
||||||
tm = type_map.get(key)
|
tm = type_map.get(key)
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
val = tm(val)
|
val = tm(val)
|
||||||
@@ -223,38 +274,42 @@ def parse_config(lines):
|
|||||||
with open(
|
with open(
|
||||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kitty.conf')
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kitty.conf')
|
||||||
) as f:
|
) as f:
|
||||||
defaults = parse_config(f.readlines())
|
defaults = parse_config(f.readlines(), check_keys=False)
|
||||||
Options = namedtuple('Defaults', ','.join(defaults.keys()))
|
Options = namedtuple('Defaults', ','.join(defaults.keys()))
|
||||||
defaults = Options(**defaults)
|
defaults = Options(**defaults)
|
||||||
actions = frozenset(defaults.keymap.values())
|
actions = frozenset(defaults.keymap.values())
|
||||||
|
|
||||||
|
|
||||||
def update_dict(a, b):
|
def merge_keymaps(defaults, newvals):
|
||||||
a.update(b)
|
ans = defaults.copy()
|
||||||
return a
|
for k, v in newvals.items():
|
||||||
|
if v in {'noop', 'no-op', 'no_op'}:
|
||||||
|
ans.pop(k, None)
|
||||||
|
continue
|
||||||
|
if v in actions:
|
||||||
|
ans[k] = v
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def merge_dicts(vals, defaults):
|
def merge_dicts(defaults, newvals):
|
||||||
return {
|
ans = defaults.copy()
|
||||||
k: update_dict(v, vals.get(k, {}))
|
ans.update(newvals)
|
||||||
if isinstance(v, dict) else vals.get(k, v)
|
return ans
|
||||||
for k, v in defaults.items()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def merge_configs(ans, vals):
|
def merge_configs(defaults, vals):
|
||||||
vals['keymap'] = {
|
ans = {}
|
||||||
k: v
|
for k, v in defaults.items():
|
||||||
for k, v in vals.get('keymap', {}).items() if v in actions
|
if isinstance(v, dict):
|
||||||
}
|
newvals = vals.get(k, {})
|
||||||
remove_keys = {
|
if k == 'keymap':
|
||||||
k
|
ans['keymap'] = merge_keymaps(v, newvals)
|
||||||
for k, v in vals.get('keymap', {}).items()
|
elif k == 'send_text_map':
|
||||||
if v in ('noop', 'no-op', 'no_op')
|
ans[k] = {m: merge_dicts(mm, newvals.get(m, {})) for m, mm in v.items()}
|
||||||
}
|
else:
|
||||||
ans = merge_dicts(vals, ans)
|
ans[k] = merge_dicts(v, newvals)
|
||||||
for k in remove_keys:
|
else:
|
||||||
ans['keymap'].pop(k, None)
|
ans[k] = vals.get(k, v)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from .fast_data_types import (
|
|||||||
GLFW_KEY_LEFT_SUPER, GLFW_KEY_RIGHT_SUPER)
|
GLFW_KEY_LEFT_SUPER, GLFW_KEY_RIGHT_SUPER)
|
||||||
|
|
||||||
appname = 'kitty'
|
appname = 'kitty'
|
||||||
version = (0, 2, 7)
|
version = (0, 3, 0)
|
||||||
str_version = '.'.join(map(str, version))
|
str_version = '.'.join(map(str, version))
|
||||||
_plat = sys.platform.lower()
|
_plat = sys.platform.lower()
|
||||||
isosx = 'darwin' in _plat
|
isosx = 'darwin' in _plat
|
||||||
|
|||||||
6
kitty/cursor_fragment.glsl
Normal file
6
kitty/cursor_fragment.glsl
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
uniform vec4 color;
|
||||||
|
out vec4 final_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final_color = color;
|
||||||
|
}
|
||||||
16
kitty/cursor_vertex.glsl
Normal file
16
kitty/cursor_vertex.glsl
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
uniform vec2 xpos;
|
||||||
|
uniform vec2 ypos;
|
||||||
|
|
||||||
|
const uvec2 pos_map[] = uvec2[4](
|
||||||
|
uvec2(1, 0), // right, top
|
||||||
|
uvec2(1, 1), // right, bottom
|
||||||
|
uvec2(0, 1), // left, bottom
|
||||||
|
uvec2(0, 0) // left, top
|
||||||
|
);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uvec2 pos = pos_map[gl_VertexID];
|
||||||
|
gl_Position = vec4(xpos[pos[0]], ypos[pos[1]], 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -31,6 +31,17 @@ change_wcwidth_wrap(PyObject UNUSED *self, PyObject *use9) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
redirect_std_streams(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
char *devnull = NULL;
|
||||||
|
if (!PyArg_ParseTuple(args, "s", &devnull)) return NULL;
|
||||||
|
if (freopen(devnull, "r", stdin) == NULL) return PyErr_SetFromErrno(PyExc_EnvironmentError);
|
||||||
|
if (freopen(devnull, "w", stdout) == NULL) return PyErr_SetFromErrno(PyExc_EnvironmentError);
|
||||||
|
if (freopen(devnull, "w", stderr) == NULL) return PyErr_SetFromErrno(PyExc_EnvironmentError);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
GL_METHODS
|
GL_METHODS
|
||||||
{"drain_read", (PyCFunction)drain_read, METH_O, ""},
|
{"drain_read", (PyCFunction)drain_read, METH_O, ""},
|
||||||
@@ -38,6 +49,7 @@ static PyMethodDef module_methods[] = {
|
|||||||
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
|
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
|
||||||
{"read_bytes", (PyCFunction)read_bytes, METH_VARARGS, ""},
|
{"read_bytes", (PyCFunction)read_bytes, METH_VARARGS, ""},
|
||||||
{"read_bytes_dump", (PyCFunction)read_bytes_dump, METH_VARARGS, ""},
|
{"read_bytes_dump", (PyCFunction)read_bytes_dump, METH_VARARGS, ""},
|
||||||
|
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
|
||||||
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
|
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
|
||||||
{"change_wcwidth", (PyCFunction)change_wcwidth_wrap, METH_O, ""},
|
{"change_wcwidth", (PyCFunction)change_wcwidth_wrap, METH_O, ""},
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ typedef unsigned int index_type;
|
|||||||
|
|
||||||
#define CELL_FIELD_COUNT 5
|
#define CELL_FIELD_COUNT 5
|
||||||
#define CELL_SIZE (CELL_FIELD_COUNT * 4)
|
#define CELL_SIZE (CELL_FIELD_COUNT * 4)
|
||||||
// The data cell size must be a multiple of 3
|
#define DATA_CELL_SIZE 6
|
||||||
#define DATA_CELL_SIZE 2 * 3
|
|
||||||
|
|
||||||
#define CHAR_MASK 0xFFFFFF
|
#define CHAR_MASK 0xFFFFFF
|
||||||
#define ATTRS_SHIFT 24
|
#define ATTRS_SHIFT 24
|
||||||
|
|||||||
143
kitty/gl.h
143
kitty/gl.h
@@ -84,6 +84,16 @@ Uniform2ui(PyObject UNUSED *self, PyObject *args) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
Uniform2i(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
int location;
|
||||||
|
int x, y;
|
||||||
|
if (!PyArg_ParseTuple(args, "iii", &location, &x, &y)) return NULL;
|
||||||
|
glUniform2i(location, x, y);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
Uniform1i(PyObject UNUSED *self, PyObject *args) {
|
Uniform1i(PyObject UNUSED *self, PyObject *args) {
|
||||||
int location;
|
int location;
|
||||||
@@ -147,7 +157,6 @@ _glewInit(PyObject UNUSED *self) {
|
|||||||
return NULL; \
|
return NULL; \
|
||||||
}
|
}
|
||||||
ARB_TEST(texture_storage);
|
ARB_TEST(texture_storage);
|
||||||
ARB_TEST(texture_buffer_object_rgb32);
|
|
||||||
#undef ARB_TEST
|
#undef ARB_TEST
|
||||||
#endif
|
#endif
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
@@ -528,29 +537,37 @@ GetTexImage(PyObject UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
NamedBufferData(PyObject UNUSED *self, PyObject *args) {
|
GetBufferSubData(PyObject UNUSED *self, PyObject *args) {
|
||||||
int usage;
|
int buftype;
|
||||||
unsigned long size, target;
|
unsigned long size, target, offset;
|
||||||
PyObject *address;
|
PyObject *address;
|
||||||
if (!PyArg_ParseTuple(args, "kkO!i", &target, &size, &PyLong_Type, &address, &usage)) return NULL;
|
if (!PyArg_ParseTuple(args, "ikkkO!", &buftype, &target, &size, &offset, &PyLong_Type, &address)) return NULL;
|
||||||
void *data = PyLong_AsVoidPtr(address);
|
void *data = PyLong_AsVoidPtr(address);
|
||||||
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
||||||
|
glBindBuffer(buftype, target);
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
#ifdef glNamedBufferData
|
glGetBufferSubData(buftype, offset, size, data);
|
||||||
#ifdef __APPLE__
|
|
||||||
if(false) {
|
|
||||||
#else
|
|
||||||
if(GLEW_VERSION_4_5) {
|
|
||||||
#endif
|
|
||||||
glNamedBufferData(target, size, data, usage);
|
|
||||||
} else {
|
|
||||||
glBindBuffer(GL_TEXTURE_BUFFER, target); glBufferData(GL_TEXTURE_BUFFER, size, data, usage); glBindBuffer(GL_TEXTURE_BUFFER, 0);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
glBindBuffer(GL_TEXTURE_BUFFER, target); glBufferData(GL_TEXTURE_BUFFER, size, data, usage); glBindBuffer(GL_TEXTURE_BUFFER, 0);
|
|
||||||
#endif
|
|
||||||
Py_END_ALLOW_THREADS;
|
Py_END_ALLOW_THREADS;
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
|
glBindBuffer(buftype, 0);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
replace_or_create_buffer(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
int usage, buftype;
|
||||||
|
unsigned long size, prev_sz, target;
|
||||||
|
PyObject *address;
|
||||||
|
if (!PyArg_ParseTuple(args, "kkkO!ii", &target, &size, &prev_sz, &PyLong_Type, &address, &usage, &buftype)) return NULL;
|
||||||
|
void *data = PyLong_AsVoidPtr(address);
|
||||||
|
if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; }
|
||||||
|
glBindBuffer(buftype, target);
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
if (prev_sz == 0 || prev_sz != size) glBufferData(buftype, size, data, usage);
|
||||||
|
else glBufferSubData(buftype, 0, size, data);
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
CHECK_ERROR;
|
||||||
|
glBindBuffer(buftype, 0);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -606,6 +623,14 @@ DeleteBuffer(PyObject UNUSED *self, PyObject *val) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
DeleteVertexArray(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
GLuint tex_id = (GLuint)PyLong_AsUnsignedLong(val);
|
||||||
|
glDeleteVertexArrays(1, &tex_id);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
BlendFunc(PyObject UNUSED *self, PyObject *args) {
|
BlendFunc(PyObject UNUSED *self, PyObject *args) {
|
||||||
int s, d;
|
int s, d;
|
||||||
@@ -641,17 +666,70 @@ EnableVertexAttribArray(PyObject UNUSED *self, PyObject *val) {
|
|||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
VertexAttribPointer(PyObject UNUSED *self, PyObject *args) {
|
VertexAttribPointer(PyObject UNUSED *self, PyObject *args) {
|
||||||
unsigned int index, stride;
|
unsigned int index;
|
||||||
int type=GL_FLOAT, normalized, size;
|
int type, normalized, size, stride;
|
||||||
void *offset;
|
unsigned long offset;
|
||||||
PyObject *l;
|
if (!PyArg_ParseTuple(args, "Iiipik", &index, &size, &type, &normalized, &stride, &offset)) return NULL;
|
||||||
if (!PyArg_ParseTuple(args, "IiipIO!", &index, &size, &type, &normalized, &stride, &PyLong_Type, &l)) return NULL;
|
switch(type) {
|
||||||
offset = PyLong_AsVoidPtr(l);
|
case GL_BYTE:
|
||||||
glVertexAttribPointer(index, size, type, normalized, stride, offset);
|
case GL_UNSIGNED_BYTE:
|
||||||
|
case GL_SHORT:
|
||||||
|
case GL_UNSIGNED_SHORT:
|
||||||
|
case GL_INT:
|
||||||
|
case GL_UNSIGNED_INT:
|
||||||
|
glVertexAttribIPointer(index, size, type, stride, (GLvoid*)offset);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
glVertexAttribPointer(index, size, type, normalized ? GL_TRUE : GL_FALSE, stride, (GLvoid*)offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
VertexAttribDivisor(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
unsigned int index, divisor;
|
||||||
|
if (!PyArg_ParseTuple(args, "II", &index, &divisor)) return NULL;
|
||||||
|
glVertexAttribDivisor(index, divisor);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
Flush(PyObject UNUSED *self) {
|
||||||
|
glFinish();
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
Finish(PyObject UNUSED *self) {
|
||||||
|
glFinish();
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
check_for_extensions(PyObject UNUSED *self) {
|
||||||
|
GLint n = 0, i, left = 2;
|
||||||
|
glGetIntegerv(GL_NUM_EXTENSIONS, &n);
|
||||||
|
bool texture_storage = false;
|
||||||
|
#define CHECK(name) if (!name) { \
|
||||||
|
if (strstr((const char*)ext, "GL_ARB_" #name) == (const char *)ext) { left--; name = true; } \
|
||||||
|
}
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
const GLubyte *ext = glGetStringi(GL_EXTENSIONS, i);
|
||||||
|
CHECK(texture_storage);
|
||||||
|
if (left < 1) break;
|
||||||
|
}
|
||||||
|
#undef CHECK
|
||||||
|
if (left > 0) {
|
||||||
|
#define CHECK(name) if (!name) { PyErr_Format(PyExc_RuntimeError, "The OpenGL driver on this system is missing the required extension: GL_ARB_%s", #name); return NULL; }
|
||||||
|
CHECK(texture_storage);
|
||||||
|
#undef CHECK
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
int add_module_gl_constants(PyObject *module) {
|
int add_module_gl_constants(PyObject *module) {
|
||||||
#define GLC(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return 0; }
|
#define GLC(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return 0; }
|
||||||
GLC(GL_VERSION);
|
GLC(GL_VERSION);
|
||||||
@@ -667,17 +745,17 @@ int add_module_gl_constants(PyObject *module) {
|
|||||||
GLC(GL_COMPILE_STATUS);
|
GLC(GL_COMPILE_STATUS);
|
||||||
GLC(GL_LINK_STATUS);
|
GLC(GL_LINK_STATUS);
|
||||||
GLC(GL_TEXTURE0); GLC(GL_TEXTURE1); GLC(GL_TEXTURE2); GLC(GL_TEXTURE3); GLC(GL_TEXTURE4); GLC(GL_TEXTURE5); GLC(GL_TEXTURE6); GLC(GL_TEXTURE7); GLC(GL_TEXTURE8);
|
GLC(GL_TEXTURE0); GLC(GL_TEXTURE1); GLC(GL_TEXTURE2); GLC(GL_TEXTURE3); GLC(GL_TEXTURE4); GLC(GL_TEXTURE5); GLC(GL_TEXTURE6); GLC(GL_TEXTURE7); GLC(GL_TEXTURE8);
|
||||||
GLC(GL_MAX_ARRAY_TEXTURE_LAYERS);
|
GLC(GL_MAX_ARRAY_TEXTURE_LAYERS); GLC(GL_TEXTURE_BINDING_BUFFER); GLC(GL_MAX_TEXTURE_BUFFER_SIZE);
|
||||||
GLC(GL_MAX_TEXTURE_SIZE);
|
GLC(GL_MAX_TEXTURE_SIZE);
|
||||||
GLC(GL_TEXTURE_2D_ARRAY);
|
GLC(GL_TEXTURE_2D_ARRAY);
|
||||||
GLC(GL_LINEAR); GLC(GL_CLAMP_TO_EDGE); GLC(GL_NEAREST);
|
GLC(GL_LINEAR); GLC(GL_CLAMP_TO_EDGE); GLC(GL_NEAREST);
|
||||||
GLC(GL_TEXTURE_MIN_FILTER); GLC(GL_TEXTURE_MAG_FILTER);
|
GLC(GL_TEXTURE_MIN_FILTER); GLC(GL_TEXTURE_MAG_FILTER);
|
||||||
GLC(GL_TEXTURE_WRAP_S); GLC(GL_TEXTURE_WRAP_T);
|
GLC(GL_TEXTURE_WRAP_S); GLC(GL_TEXTURE_WRAP_T);
|
||||||
GLC(GL_UNPACK_ALIGNMENT);
|
GLC(GL_UNPACK_ALIGNMENT);
|
||||||
GLC(GL_R8); GLC(GL_RED); GLC(GL_UNSIGNED_BYTE); GLC(GL_RGB32UI); GLC(GL_RGBA);
|
GLC(GL_R8); GLC(GL_RED); GLC(GL_UNSIGNED_BYTE); GLC(GL_R32UI); GLC(GL_RGB32UI); GLC(GL_RGBA);
|
||||||
GLC(GL_TEXTURE_BUFFER); GLC(GL_STATIC_DRAW); GLC(GL_STREAM_DRAW);
|
GLC(GL_TEXTURE_BUFFER); GLC(GL_STATIC_DRAW); GLC(GL_STREAM_DRAW);
|
||||||
GLC(GL_SRC_ALPHA); GLC(GL_ONE_MINUS_SRC_ALPHA);
|
GLC(GL_SRC_ALPHA); GLC(GL_ONE_MINUS_SRC_ALPHA);
|
||||||
GLC(GL_BLEND); GLC(GL_FLOAT); GLC(GL_ARRAY_BUFFER);
|
GLC(GL_BLEND); GLC(GL_FLOAT); GLC(GL_UNSIGNED_INT); GLC(GL_ARRAY_BUFFER);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -685,13 +763,16 @@ int add_module_gl_constants(PyObject *module) {
|
|||||||
#define GL_METHODS \
|
#define GL_METHODS \
|
||||||
{"enable_automatic_opengl_error_checking", (PyCFunction)enable_automatic_error_checking, METH_O, NULL}, \
|
{"enable_automatic_opengl_error_checking", (PyCFunction)enable_automatic_error_checking, METH_O, NULL}, \
|
||||||
{"copy_image_sub_data", (PyCFunction)copy_image_sub_data, METH_VARARGS, NULL}, \
|
{"copy_image_sub_data", (PyCFunction)copy_image_sub_data, METH_VARARGS, NULL}, \
|
||||||
|
{"replace_or_create_buffer", (PyCFunction)replace_or_create_buffer, METH_VARARGS, NULL}, \
|
||||||
{"glewInit", (PyCFunction)_glewInit, METH_NOARGS, NULL}, \
|
{"glewInit", (PyCFunction)_glewInit, METH_NOARGS, NULL}, \
|
||||||
|
{"check_for_extensions", (PyCFunction)check_for_extensions, METH_NOARGS, NULL}, \
|
||||||
METH(Viewport, METH_VARARGS) \
|
METH(Viewport, METH_VARARGS) \
|
||||||
METH(CheckError, METH_NOARGS) \
|
METH(CheckError, METH_NOARGS) \
|
||||||
METH(ClearColor, METH_VARARGS) \
|
METH(ClearColor, METH_VARARGS) \
|
||||||
METH(GetProgramiv, METH_VARARGS) \
|
METH(GetProgramiv, METH_VARARGS) \
|
||||||
METH(GetShaderiv, METH_VARARGS) \
|
METH(GetShaderiv, METH_VARARGS) \
|
||||||
METH(Uniform2ui, METH_VARARGS) \
|
METH(Uniform2ui, METH_VARARGS) \
|
||||||
|
METH(Uniform2i, METH_VARARGS) \
|
||||||
METH(Uniform1i, METH_VARARGS) \
|
METH(Uniform1i, METH_VARARGS) \
|
||||||
METH(Uniform2f, METH_VARARGS) \
|
METH(Uniform2f, METH_VARARGS) \
|
||||||
METH(Uniform4f, METH_VARARGS) \
|
METH(Uniform4f, METH_VARARGS) \
|
||||||
@@ -702,6 +783,7 @@ int add_module_gl_constants(PyObject *module) {
|
|||||||
METH(CompileShader, METH_O) \
|
METH(CompileShader, METH_O) \
|
||||||
METH(DeleteTexture, METH_O) \
|
METH(DeleteTexture, METH_O) \
|
||||||
METH(DeleteBuffer, METH_O) \
|
METH(DeleteBuffer, METH_O) \
|
||||||
|
METH(DeleteVertexArray, METH_O) \
|
||||||
METH(GetString, METH_O) \
|
METH(GetString, METH_O) \
|
||||||
METH(GetIntegerv, METH_O) \
|
METH(GetIntegerv, METH_O) \
|
||||||
METH(Clear, METH_O) \
|
METH(Clear, METH_O) \
|
||||||
@@ -718,6 +800,7 @@ int add_module_gl_constants(PyObject *module) {
|
|||||||
METH(Disable, METH_O) \
|
METH(Disable, METH_O) \
|
||||||
METH(EnableVertexAttribArray, METH_O) \
|
METH(EnableVertexAttribArray, METH_O) \
|
||||||
METH(VertexAttribPointer, METH_VARARGS) \
|
METH(VertexAttribPointer, METH_VARARGS) \
|
||||||
|
METH(VertexAttribDivisor, METH_VARARGS) \
|
||||||
METH(GetProgramInfoLog, METH_O) \
|
METH(GetProgramInfoLog, METH_O) \
|
||||||
METH(GetShaderInfoLog, METH_O) \
|
METH(GetShaderInfoLog, METH_O) \
|
||||||
METH(ActiveTexture, METH_O) \
|
METH(ActiveTexture, METH_O) \
|
||||||
@@ -735,6 +818,8 @@ int add_module_gl_constants(PyObject *module) {
|
|||||||
METH(CopyImageSubData, METH_VARARGS) \
|
METH(CopyImageSubData, METH_VARARGS) \
|
||||||
METH(TexSubImage3D, METH_VARARGS) \
|
METH(TexSubImage3D, METH_VARARGS) \
|
||||||
METH(GetTexImage, METH_VARARGS) \
|
METH(GetTexImage, METH_VARARGS) \
|
||||||
METH(NamedBufferData, METH_VARARGS) \
|
METH(GetBufferSubData, METH_VARARGS) \
|
||||||
METH(BlendFunc, METH_VARARGS) \
|
METH(BlendFunc, METH_VARARGS) \
|
||||||
|
METH(Finish, METH_NOARGS) \
|
||||||
|
METH(Flush, METH_NOARGS) \
|
||||||
|
|
||||||
|
|||||||
16
kitty/glfw.c
16
kitty/glfw.c
@@ -208,6 +208,18 @@ glfw_get_key_name(PyObject UNUSED *self, PyObject *args) {
|
|||||||
return Py_BuildValue("s", glfwGetKeyName(key, scancode));
|
return Py_BuildValue("s", glfwGetKeyName(key, scancode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject*
|
||||||
|
glfw_init_hint_string(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
int hint_id;
|
||||||
|
char *hint;
|
||||||
|
if (!PyArg_ParseTuple(args, "is", &hint_id, &hint)) return NULL;
|
||||||
|
#ifdef glfwInitHintString
|
||||||
|
glfwInitHintString(hint_id, hint);
|
||||||
|
#endif
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -448,6 +460,10 @@ init_glfw(PyObject *m) {
|
|||||||
PyEval_InitThreads();
|
PyEval_InitThreads();
|
||||||
glfwSetErrorCallback(cb_error_callback);
|
glfwSetErrorCallback(cb_error_callback);
|
||||||
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
|
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
|
||||||
|
#ifdef GLFW_X11_WM_CLASS_NAME
|
||||||
|
ADDC(GLFW_X11_WM_CLASS_NAME)
|
||||||
|
ADDC(GLFW_X11_WM_CLASS_CLASS)
|
||||||
|
#endif
|
||||||
ADDC(GLFW_RELEASE);
|
ADDC(GLFW_RELEASE);
|
||||||
ADDC(GLFW_PRESS);
|
ADDC(GLFW_PRESS);
|
||||||
ADDC(GLFW_REPEAT);
|
ADDC(GLFW_REPEAT);
|
||||||
|
|||||||
20
kitty/glfw.h
20
kitty/glfw.h
@@ -17,15 +17,20 @@ PyObject* glfw_wait_events(PyObject UNUSED *self, PyObject*);
|
|||||||
PyObject* glfw_post_empty_event(PyObject UNUSED *self);
|
PyObject* glfw_post_empty_event(PyObject UNUSED *self);
|
||||||
PyObject* glfw_get_physical_dpi(PyObject UNUSED *self);
|
PyObject* glfw_get_physical_dpi(PyObject UNUSED *self);
|
||||||
PyObject* glfw_get_key_name(PyObject UNUSED *self, PyObject *args);
|
PyObject* glfw_get_key_name(PyObject UNUSED *self, PyObject *args);
|
||||||
|
PyObject* glfw_init_hint_string(PyObject UNUSED *self, PyObject *args);
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
PyObject* cocoa_hide_titlebar(PyObject UNUSED *self, PyObject *window_id);
|
|
||||||
PyObject* cocoa_get_lang(PyObject UNUSED *self);
|
PyObject* cocoa_get_lang(PyObject UNUSED *self);
|
||||||
#define COCOA_HIDE_TITLEBAR {"cocoa_hide_titlebar", (PyCFunction)cocoa_hide_titlebar, METH_O, ""},
|
PyObject* cocoa_make_window_resizable(PyObject UNUSED *self, PyObject *window_id);
|
||||||
#define COCOA_GET_LANG {"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
|
PyObject* cocoa_create_global_menu(PyObject UNUSED *self);
|
||||||
|
PyObject* cocoa_update_title(PyObject UNUSED *self, PyObject *title);
|
||||||
|
#define COCOA_FUNCS \
|
||||||
|
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""}, \
|
||||||
|
{"cocoa_make_window_resizable", (PyCFunction)cocoa_make_window_resizable, METH_O, ""}, \
|
||||||
|
{"cocoa_create_global_menu", (PyCFunction)cocoa_create_global_menu, METH_NOARGS, ""}, \
|
||||||
|
{"cocoa_update_title", (PyCFunction)cocoa_update_title, METH_O, ""},
|
||||||
#else
|
#else
|
||||||
#define COCOA_HIDE_TITLEBAR
|
#define COCOA_FUNCS
|
||||||
#define COCOA_GET_LANG
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define GLFW_FUNC_WRAPPERS \
|
#define GLFW_FUNC_WRAPPERS \
|
||||||
@@ -38,6 +43,5 @@ PyObject* cocoa_get_lang(PyObject UNUSED *self);
|
|||||||
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""}, \
|
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""}, \
|
||||||
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""}, \
|
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""}, \
|
||||||
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""}, \
|
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""}, \
|
||||||
COCOA_HIDE_TITLEBAR \
|
{"glfw_init_hint_string", (PyCFunction)glfw_init_hint_string, METH_VARARGS, ""}, \
|
||||||
COCOA_GET_LANG
|
COCOA_FUNCS
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,12 @@ def get_key_map(screen):
|
|||||||
return cursor_key_mode_map[screen.cursor_key_mode]
|
return cursor_key_mode_map[screen.cursor_key_mode]
|
||||||
|
|
||||||
|
|
||||||
|
def keyboard_mode_name(screen):
|
||||||
|
if screen.extended_keyboard:
|
||||||
|
return 'kitty'
|
||||||
|
return 'application' if screen.cursor_key_mode else 'normal'
|
||||||
|
|
||||||
|
|
||||||
valid_localized_key_names = {
|
valid_localized_key_names = {
|
||||||
k: getattr(defines, 'GLFW_KEY_' + k)
|
k: getattr(defines, 'GLFW_KEY_' + k)
|
||||||
for k in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
for k in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||||
@@ -195,3 +201,11 @@ def interpret_text_event(codepoint, mods, window):
|
|||||||
def get_shortcut(keymap, mods, key, scancode):
|
def get_shortcut(keymap, mods, key, scancode):
|
||||||
key = get_localized_key(key, scancode)
|
key = get_localized_key(key, scancode)
|
||||||
return keymap.get((mods & 0b1111, key))
|
return keymap.get((mods & 0b1111, key))
|
||||||
|
|
||||||
|
|
||||||
|
def get_sent_data(send_text_map, key, scancode, mods, window, action):
|
||||||
|
if action in (defines.GLFW_PRESS, defines.GLFW_REPEAT):
|
||||||
|
key = get_localized_key(key, scancode)
|
||||||
|
m = keyboard_mode_name(window.screen)
|
||||||
|
keymap = send_text_map[m]
|
||||||
|
return keymap.get((mods & 0b1111, key))
|
||||||
|
|||||||
@@ -231,6 +231,23 @@ map ctrl+shift+equal increase_font_size
|
|||||||
map ctrl+shift+minus decrease_font_size
|
map ctrl+shift+minus decrease_font_size
|
||||||
map ctrl+shift+backspace restore_font_size
|
map ctrl+shift+backspace restore_font_size
|
||||||
|
|
||||||
|
# Sending arbitrary text on shortcut key presses
|
||||||
|
# You can tell kitty to send arbitrary (UTF-8) encoded text to
|
||||||
|
# the client program when pressing specified shortcut keys. For example:
|
||||||
|
# send_text all ctrl+alt+a Special text
|
||||||
|
# This will send "Special text" when you press the Ctrl+Alt+a key combination.
|
||||||
|
# The text to be sent is a python string literal so you can use escapes like
|
||||||
|
# \x1b to send control codes or \u21fb to send unicode characters (or you can
|
||||||
|
# just input the unicode characters directly as UTF-8 text). The first argument
|
||||||
|
# to send_text is the keyboard modes in which to activate the shortcut. The possible
|
||||||
|
# values are normal or application or kitty or a comma separated combination of them.
|
||||||
|
# The special keyword all means all modes. The modes normal and application refer to
|
||||||
|
# the DECCKM cursor key mode for terminals, and kitty refers to the special kitty
|
||||||
|
# extended keyboard protocol. Another example, that outputs a word and then moves the cursor
|
||||||
|
# to the start of the line (same as pressing the Home key):
|
||||||
|
# send_text normal ctrl+alt+a Word\x1b[H
|
||||||
|
# send_text application ctrl+alt+a Word\x1bOH
|
||||||
|
|
||||||
# Symbol mapping (special font for specified unicode code points). Map the
|
# Symbol mapping (special font for specified unicode code points). Map the
|
||||||
# specified unicode codepoints to a particular font. Useful if you need special
|
# specified unicode codepoints to a particular font. Useful if you need special
|
||||||
# rendering for some symbols, such as for Powerline. Avoids the need for
|
# rendering for some symbols, such as for Powerline. Avoids the need for
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
from queue import Empty
|
from queue import Empty
|
||||||
|
|
||||||
from .boss import Boss
|
from .boss import Boss
|
||||||
@@ -20,16 +19,21 @@ from .constants import (
|
|||||||
)
|
)
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
GL_COLOR_BUFFER_BIT, GLFW_CONTEXT_VERSION_MAJOR,
|
GL_COLOR_BUFFER_BIT, GLFW_CONTEXT_VERSION_MAJOR,
|
||||||
GLFW_CONTEXT_VERSION_MINOR, GLFW_OPENGL_CORE_PROFILE,
|
GLFW_CONTEXT_VERSION_MINOR, GLFW_DECORATED, GLFW_OPENGL_CORE_PROFILE,
|
||||||
GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_PROFILE, GLFW_SAMPLES,
|
GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_PROFILE, GLFW_SAMPLES,
|
||||||
GLFW_STENCIL_BITS, Window, change_wcwidth,
|
GLFW_STENCIL_BITS, Window, change_wcwidth, check_for_extensions,
|
||||||
enable_automatic_opengl_error_checking, glClear, glClearColor, glewInit,
|
enable_automatic_opengl_error_checking, glClear, glClearColor, glewInit,
|
||||||
glfw_init, glfw_set_error_callback, glfw_swap_interval, glfw_terminate,
|
glfw_init, glfw_init_hint_string, glfw_set_error_callback,
|
||||||
glfw_wait_events, glfw_window_hint
|
glfw_swap_interval, glfw_terminate, glfw_wait_events, glfw_window_hint
|
||||||
)
|
)
|
||||||
from .layout import all_layouts
|
from .layout import all_layouts
|
||||||
from .shaders import GL_VERSION
|
from .shaders import GL_VERSION
|
||||||
from .utils import safe_print
|
from .utils import detach, safe_print
|
||||||
|
|
||||||
|
try:
|
||||||
|
from .fast_data_types import GLFW_X11_WM_CLASS_NAME, GLFW_X11_WM_CLASS_CLASS
|
||||||
|
except ImportError:
|
||||||
|
GLFW_X11_WM_CLASS_NAME = GLFW_X11_WM_CLASS_CLASS = None
|
||||||
|
|
||||||
|
|
||||||
defconf = os.path.join(config_dir, 'kitty.conf')
|
defconf = os.path.join(config_dir, 'kitty.conf')
|
||||||
@@ -95,6 +99,13 @@ def option_parser():
|
|||||||
default=False,
|
default=False,
|
||||||
help=_('Output commands received from child process to stdout')
|
help=_('Output commands received from child process to stdout')
|
||||||
)
|
)
|
||||||
|
if not isosx:
|
||||||
|
a(
|
||||||
|
'--detach',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_('Detach from the controlling terminal, if any')
|
||||||
|
)
|
||||||
a(
|
a(
|
||||||
'--replay-commands',
|
'--replay-commands',
|
||||||
default=None,
|
default=None,
|
||||||
@@ -137,7 +148,9 @@ def option_parser():
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def setup_opengl():
|
def setup_opengl(opts):
|
||||||
|
if opts.macos_hide_titlebar:
|
||||||
|
glfw_window_hint(GLFW_DECORATED, False)
|
||||||
glfw_window_hint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION[0])
|
glfw_window_hint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION[0])
|
||||||
glfw_window_hint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSION[1])
|
glfw_window_hint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSION[1])
|
||||||
glfw_window_hint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
|
glfw_window_hint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
|
||||||
@@ -175,7 +188,7 @@ def dispatch_pending_calls(boss):
|
|||||||
|
|
||||||
|
|
||||||
def run_app(opts, args):
|
def run_app(opts, args):
|
||||||
setup_opengl()
|
setup_opengl(opts)
|
||||||
load_cached_values()
|
load_cached_values()
|
||||||
if 'window-size' in cached_values and opts.remember_window_size:
|
if 'window-size' in cached_values and opts.remember_window_size:
|
||||||
ws = cached_values['window-size']
|
ws = cached_values['window-size']
|
||||||
@@ -188,13 +201,21 @@ def run_app(opts, args):
|
|||||||
else:
|
else:
|
||||||
viewport_size.width = opts.initial_window_width
|
viewport_size.width = opts.initial_window_width
|
||||||
viewport_size.height = opts.initial_window_height
|
viewport_size.height = opts.initial_window_height
|
||||||
window = Window(viewport_size.width, viewport_size.height, args.cls)
|
try:
|
||||||
|
window = Window(viewport_size.width, viewport_size.height, args.cls)
|
||||||
|
except ValueError:
|
||||||
|
safe_print('Failed to create GLFW window with initial size:', viewport_size)
|
||||||
|
viewport_size.width = 640
|
||||||
|
viewport_size.height = 400
|
||||||
|
window = Window(viewport_size.width, viewport_size.height, args.cls)
|
||||||
window.set_title(appname)
|
window.set_title(appname)
|
||||||
window.make_context_current()
|
window.make_context_current()
|
||||||
if isosx:
|
if isosx:
|
||||||
|
from .fast_data_types import cocoa_make_window_resizable, cocoa_create_global_menu
|
||||||
|
check_for_extensions()
|
||||||
|
cocoa_create_global_menu()
|
||||||
if opts.macos_hide_titlebar:
|
if opts.macos_hide_titlebar:
|
||||||
from .fast_data_types import cocoa_hide_titlebar
|
cocoa_make_window_resizable(window.cocoa_window_id())
|
||||||
cocoa_hide_titlebar(window.cocoa_window_id())
|
|
||||||
else:
|
else:
|
||||||
with open(logo_data_file, 'rb') as f:
|
with open(logo_data_file, 'rb') as f:
|
||||||
window.set_icon(f.read(), 256, 256)
|
window.set_icon(f.read(), 256, 256)
|
||||||
@@ -241,11 +262,25 @@ def ensure_osx_locale():
|
|||||||
def main():
|
def main():
|
||||||
if isosx:
|
if isosx:
|
||||||
ensure_osx_locale()
|
ensure_osx_locale()
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
try:
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
except Exception:
|
||||||
|
if not isosx:
|
||||||
|
raise
|
||||||
|
print('Failed to set locale with LANG:', os.environ.get('LANG'), file=sys.stderr)
|
||||||
|
os.environ.pop('LANG')
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
except Exception:
|
||||||
|
print('Failed to set locale with no LANG, ignoring', file=sys.stderr)
|
||||||
if os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES',
|
if os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES',
|
||||||
None) == '1' and getattr(sys, 'frozen', True):
|
None) == '1' and getattr(sys, 'frozen', True):
|
||||||
os.chdir(os.path.expanduser('~'))
|
os.chdir(os.path.expanduser('~'))
|
||||||
|
if not os.path.isdir(os.getcwd()):
|
||||||
|
os.chdir(os.path.expanduser('~'))
|
||||||
args = option_parser().parse_args()
|
args = option_parser().parse_args()
|
||||||
|
if getattr(args, 'detach', False):
|
||||||
|
detach()
|
||||||
if args.cmd:
|
if args.cmd:
|
||||||
exec(args.cmd)
|
exec(args.cmd)
|
||||||
return
|
return
|
||||||
@@ -259,6 +294,8 @@ def main():
|
|||||||
change_wcwidth(not opts.use_system_wcwidth)
|
change_wcwidth(not opts.use_system_wcwidth)
|
||||||
glfw_set_error_callback(on_glfw_error)
|
glfw_set_error_callback(on_glfw_error)
|
||||||
enable_automatic_opengl_error_checking(args.debug_gl)
|
enable_automatic_opengl_error_checking(args.debug_gl)
|
||||||
|
if GLFW_X11_WM_CLASS_CLASS is not None:
|
||||||
|
glfw_init_hint_string(GLFW_X11_WM_CLASS_CLASS, opts.cls)
|
||||||
if not glfw_init():
|
if not glfw_init():
|
||||||
raise SystemExit('GLFW initialization failed')
|
raise SystemExit('GLFW initialization failed')
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -58,5 +58,5 @@ def encode_mouse_event(tracking_mode, tracking_protocol, button, action, mods, x
|
|||||||
ans = bytes(ans)
|
ans = bytes(ans)
|
||||||
else:
|
else:
|
||||||
if x <= 223 and y <= 223:
|
if x <= 223 and y <= 223:
|
||||||
ans = bytearray([0o33, ord('['), cb + 32, x + 32, y + 32])
|
ans = bytearray([0o33, ord('['), ord('M'), cb + 32, x + 32, y + 32])
|
||||||
return ans
|
return ans
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
#include <structmember.h>
|
#include <structmember.h>
|
||||||
|
#include <limits.h>
|
||||||
#include "unicode-data.h"
|
#include "unicode-data.h"
|
||||||
#include "tracker.h"
|
#include "tracker.h"
|
||||||
#include "modes.h"
|
#include "modes.h"
|
||||||
@@ -1332,6 +1333,14 @@ static PyGetSetDef getsetters[] = {
|
|||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if UINT_MAX == UINT32_MAX
|
||||||
|
#define T_COL T_UINT
|
||||||
|
#elif ULONG_MAX == UINT32_MAX
|
||||||
|
#define T_COL T_ULONG
|
||||||
|
#else
|
||||||
|
#error Neither int nor long is 4-bytes in size
|
||||||
|
#endif
|
||||||
|
|
||||||
static PyMemberDef members[] = {
|
static PyMemberDef members[] = {
|
||||||
{"callbacks", T_OBJECT_EX, offsetof(Screen, callbacks), 0, "callbacks"},
|
{"callbacks", T_OBJECT_EX, offsetof(Screen, callbacks), 0, "callbacks"},
|
||||||
{"cursor", T_OBJECT_EX, offsetof(Screen, cursor), READONLY, "cursor"},
|
{"cursor", T_OBJECT_EX, offsetof(Screen, cursor), READONLY, "cursor"},
|
||||||
@@ -1341,11 +1350,11 @@ static PyMemberDef members[] = {
|
|||||||
{"columns", T_UINT, offsetof(Screen, columns), READONLY, "columns"},
|
{"columns", T_UINT, offsetof(Screen, columns), READONLY, "columns"},
|
||||||
{"margin_top", T_UINT, offsetof(Screen, margin_top), READONLY, "margin_top"},
|
{"margin_top", T_UINT, offsetof(Screen, margin_top), READONLY, "margin_top"},
|
||||||
{"margin_bottom", T_UINT, offsetof(Screen, margin_bottom), READONLY, "margin_bottom"},
|
{"margin_bottom", T_UINT, offsetof(Screen, margin_bottom), READONLY, "margin_bottom"},
|
||||||
{"default_fg", T_ULONG, offsetof(Screen, default_fg), 0, "default_fg"},
|
{"default_fg", T_COL, offsetof(Screen, default_fg), 0, "default_fg"},
|
||||||
{"default_bg", T_ULONG, offsetof(Screen, default_bg), 0, "default_bg"},
|
{"default_bg", T_COL, offsetof(Screen, default_bg), 0, "default_bg"},
|
||||||
{"highlight_fg", T_ULONG, offsetof(Screen, highlight_fg), 0, "highlight_fg"},
|
{"highlight_fg", T_COL, offsetof(Screen, highlight_fg), 0, "highlight_fg"},
|
||||||
{"highlight_bg", T_ULONG, offsetof(Screen, highlight_bg), 0, "highlight_bg"},
|
{"highlight_bg", T_COL, offsetof(Screen, highlight_bg), 0, "highlight_bg"},
|
||||||
{"cursor_color", T_ULONG, offsetof(Screen, cursor_color), 0, "cursor_color"},
|
{"cursor_color", T_COL, offsetof(Screen, cursor_color), 0, "cursor_color"},
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
191
kitty/shaders.py
191
kitty/shaders.py
@@ -2,39 +2,106 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from collections import namedtuple
|
||||||
|
from contextlib import contextmanager
|
||||||
from ctypes import addressof, sizeof
|
from ctypes import addressof, sizeof
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
from .fonts.render import render_cell
|
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
glCreateProgram, glAttachShader, GL_FRAGMENT_SHADER, GL_VERTEX_SHADER,
|
BOLD, GL_ARRAY_BUFFER, GL_CLAMP_TO_EDGE, GL_COMPILE_STATUS, GL_FLOAT,
|
||||||
glLinkProgram, GL_TRUE, GL_LINK_STATUS, glGetProgramiv,
|
GL_FRAGMENT_SHADER, GL_LINK_STATUS, GL_MAX_ARRAY_TEXTURE_LAYERS,
|
||||||
glGetProgramInfoLog, glDeleteShader, glDeleteProgram, glGenVertexArrays,
|
GL_MAX_TEXTURE_SIZE, GL_NEAREST, GL_R8, GL_RED, GL_STREAM_DRAW,
|
||||||
glCreateShader, glShaderSource, glCompileShader, glGetShaderiv,
|
GL_TEXTURE0, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER,
|
||||||
GL_COMPILE_STATUS, glGetShaderInfoLog, glGetUniformLocation,
|
GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_TRUE,
|
||||||
glGetAttribLocation, glUseProgram, glBindVertexArray, GL_TEXTURE0,
|
GL_UNPACK_ALIGNMENT, GL_UNSIGNED_BYTE, GL_VERTEX_SHADER, ITALIC, SpriteMap,
|
||||||
GL_TEXTURE1, glGetIntegerv, GL_MAX_ARRAY_TEXTURE_LAYERS, glNamedBufferData,
|
copy_image_sub_data, glActiveTexture, glAttachShader, glBindBuffer,
|
||||||
GL_MAX_TEXTURE_SIZE, glDeleteTexture, GL_TEXTURE_2D_ARRAY, glGenTextures,
|
glBindTexture, glBindVertexArray, glCompileShader, glCopyImageSubData,
|
||||||
glBindTexture, glTexParameteri, GL_CLAMP_TO_EDGE, glDeleteBuffer,
|
glCreateProgram, glCreateShader, glDeleteBuffer, glDeleteProgram,
|
||||||
GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S,
|
glDeleteShader, glDeleteTexture, glDeleteVertexArray,
|
||||||
GL_NEAREST, GL_TEXTURE_WRAP_T, glGenBuffers, GL_R8, GL_RED,
|
glEnableVertexAttribArray, glGenBuffers, glGenTextures, glGenVertexArrays,
|
||||||
GL_UNPACK_ALIGNMENT, GL_UNSIGNED_BYTE, GL_STATIC_DRAW, GL_STREAM_DRAW,
|
glGetAttribLocation, glGetBufferSubData, glGetIntegerv,
|
||||||
GL_TEXTURE_BUFFER, GL_RGB32UI, GL_FLOAT, GL_ARRAY_BUFFER, glBindBuffer,
|
glGetProgramInfoLog, glGetProgramiv, glGetShaderInfoLog, glGetShaderiv,
|
||||||
glPixelStorei, glTexBuffer, glActiveTexture, glTexStorage3D,
|
glGetUniformLocation, glLinkProgram, glPixelStorei, glShaderSource,
|
||||||
glCopyImageSubData, glTexSubImage3D, ITALIC, BOLD, SpriteMap,
|
glTexParameteri, glTexStorage3D, glTexSubImage3D, glUseProgram,
|
||||||
glEnableVertexAttribArray, glVertexAttribPointer, copy_image_sub_data
|
glVertexAttribDivisor, glVertexAttribPointer, replace_or_create_buffer
|
||||||
)
|
)
|
||||||
|
from .fonts.render import render_cell
|
||||||
from .utils import safe_print
|
from .utils import safe_print
|
||||||
|
|
||||||
GL_VERSION = (3, 3)
|
GL_VERSION = (3, 3)
|
||||||
VERSION = GL_VERSION[0] * 100 + GL_VERSION[1] * 10
|
VERSION = GL_VERSION[0] * 100 + GL_VERSION[1] * 10
|
||||||
ITALIC_MASK = 1 << ITALIC
|
ITALIC_MASK = 1 << ITALIC
|
||||||
BOLD_MASK = 1 << BOLD
|
BOLD_MASK = 1 << BOLD
|
||||||
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
VertexArray = namedtuple('VertexArray', 'name size dtype normalized stride offset divisor')
|
||||||
|
|
||||||
|
|
||||||
class Sprites:
|
@lru_cache()
|
||||||
|
def load_shaders(name):
|
||||||
|
vert = open(os.path.join(BASE, '{}_vertex.glsl'.format(name))).read()
|
||||||
|
frag = open(os.path.join(BASE, '{}_fragment.glsl'.format(name))).read()
|
||||||
|
return vert, frag
|
||||||
|
|
||||||
|
|
||||||
|
class BufferManager: # {{{
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.sizes = {}
|
||||||
|
self.types = {}
|
||||||
|
self.ctypes_types = {}
|
||||||
|
self.name_count = 0
|
||||||
|
|
||||||
|
def create(self, for_use=GL_ARRAY_BUFFER):
|
||||||
|
buf_id = glGenBuffers(1)
|
||||||
|
self.types[buf_id] = for_use
|
||||||
|
self.sizes.pop(buf_id, None)
|
||||||
|
self.ctypes_types.pop(buf_id, None)
|
||||||
|
return buf_id
|
||||||
|
|
||||||
|
def delete(self, buf_id):
|
||||||
|
if buf_id in self.types:
|
||||||
|
glDeleteBuffer(buf_id)
|
||||||
|
self.sizes.pop(buf_id, None)
|
||||||
|
self.types.pop(buf_id)
|
||||||
|
self.ctypes_types.pop(buf_id, None)
|
||||||
|
|
||||||
|
def set_data(self, buf_id, data, usage=GL_STREAM_DRAW, verify=False):
|
||||||
|
prev_sz = self.sizes.get(buf_id, 0)
|
||||||
|
new_sz = sizeof(data)
|
||||||
|
replace_or_create_buffer(buf_id, new_sz, prev_sz, addressof(data), usage, self.types[buf_id])
|
||||||
|
self.sizes[buf_id] = new_sz
|
||||||
|
self.ctypes_types[buf_id] = type(data)
|
||||||
|
if verify:
|
||||||
|
verify_data = self.get_data(buf_id)
|
||||||
|
if list(data) != list(verify_data):
|
||||||
|
raise RuntimeError('OpenGL failed to upload to buffer')
|
||||||
|
|
||||||
|
def get_data(self, buf_id):
|
||||||
|
verify_data = self.ctypes_types[buf_id]()
|
||||||
|
glGetBufferSubData(self.types[buf_id], buf_id, self.sizes[buf_id], 0, addressof(verify_data))
|
||||||
|
return verify_data
|
||||||
|
|
||||||
|
def bind(self, buf_id):
|
||||||
|
glBindBuffer(self.types[buf_id], buf_id)
|
||||||
|
|
||||||
|
def unbind(self, buf_id):
|
||||||
|
glBindBuffer(self.types[buf_id], 0)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def bound_buffer(self, buf_id):
|
||||||
|
self.bind(buf_id)
|
||||||
|
yield
|
||||||
|
self.unbind(buf_id)
|
||||||
|
|
||||||
|
|
||||||
|
buffer_manager = BufferManager()
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
class Sprites: # {{{
|
||||||
''' Maintain sprite sheets of all rendered characters on the GPU as a texture
|
''' Maintain sprite sheets of all rendered characters on the GPU as a texture
|
||||||
array with each texture being a sprite sheet. '''
|
array with each texture being a sprite sheet. '''
|
||||||
|
|
||||||
@@ -43,15 +110,14 @@ class Sprites:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.xnum = self.ynum = 1
|
self.xnum = self.ynum = 1
|
||||||
self.sampler_num = 0
|
|
||||||
self.buffer_sampler_num = 1
|
|
||||||
self.first_cell_cache = {}
|
self.first_cell_cache = {}
|
||||||
self.second_cell_cache = {}
|
self.second_cell_cache = {}
|
||||||
self.x = self.y = self.z = 0
|
self.x = self.y = self.z = 0
|
||||||
self.texture_id = self.buffer_texture_id = None
|
self.texture_id = None
|
||||||
self.last_num_of_layers = 1
|
self.last_num_of_layers = 1
|
||||||
self.last_ynum = -1
|
self.last_ynum = -1
|
||||||
self.texture_unit = GL_TEXTURE0
|
self.sampler_num = 0
|
||||||
|
self.texture_unit = GL_TEXTURE0 + self.sampler_num
|
||||||
self.backend = SpriteMap(glGetIntegerv(GL_MAX_TEXTURE_SIZE), glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS))
|
self.backend = SpriteMap(glGetIntegerv(GL_MAX_TEXTURE_SIZE), glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS))
|
||||||
self.lock = Lock()
|
self.lock = Lock()
|
||||||
|
|
||||||
@@ -103,7 +169,6 @@ class Sprites:
|
|||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
|
||||||
x, y = x * self.cell_width, y * self.cell_height
|
x, y = x * self.cell_width, y * self.cell_height
|
||||||
glTexSubImage3D(tgt, 0, x, y, z, self.cell_width, self.cell_height, 1, GL_RED, GL_UNSIGNED_BYTE, addressof(buf))
|
glTexSubImage3D(tgt, 0, x, y, z, self.cell_width, self.cell_height, 1, GL_RED, GL_UNSIGNED_BYTE, addressof(buf))
|
||||||
glBindTexture(tgt, 0)
|
|
||||||
|
|
||||||
def realloc_texture(self):
|
def realloc_texture(self):
|
||||||
tgt = GL_TEXTURE_2D_ARRAY
|
tgt = GL_TEXTURE_2D_ARRAY
|
||||||
@@ -138,51 +203,28 @@ class Sprites:
|
|||||||
self.last_num_of_layers = znum
|
self.last_num_of_layers = znum
|
||||||
self.last_ynum = self.backend.ynum
|
self.last_ynum = self.backend.ynum
|
||||||
self.texture_id = tex
|
self.texture_id = tex
|
||||||
glBindTexture(tgt, 0)
|
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
if self.texture_id is not None:
|
if self.texture_id is not None:
|
||||||
glDeleteTexture(self.texture_id)
|
glDeleteTexture(self.texture_id)
|
||||||
self.texture_id = None
|
self.texture_id = None
|
||||||
if self.buffer_texture_id is not None:
|
|
||||||
glDeleteTexture(self.buffer_texture_id)
|
|
||||||
|
|
||||||
def ensure_state(self):
|
def ensure_state(self):
|
||||||
if self.texture_id is None:
|
if self.texture_id is None:
|
||||||
self.realloc_texture()
|
self.realloc_texture()
|
||||||
self.buffer_texture_id = glGenTextures(1)
|
|
||||||
self.buffer_texture_unit = GL_TEXTURE1
|
|
||||||
|
|
||||||
def add_sprite_map(self):
|
|
||||||
return glGenBuffers(1)
|
|
||||||
|
|
||||||
def set_sprite_map(self, buf_id, data, usage=GL_STREAM_DRAW):
|
|
||||||
self.bind_sprite_map(buf_id)
|
|
||||||
glNamedBufferData(buf_id, sizeof(data), addressof(data), usage)
|
|
||||||
|
|
||||||
def bind_sprite_map(self, buf_id):
|
|
||||||
glBindBuffer(GL_TEXTURE_BUFFER, buf_id)
|
|
||||||
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32UI, buf_id)
|
|
||||||
|
|
||||||
def destroy_sprite_map(self, buf_id):
|
|
||||||
glDeleteBuffer(buf_id)
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.ensure_state()
|
self.ensure_state()
|
||||||
glActiveTexture(self.texture_unit)
|
glActiveTexture(self.texture_unit)
|
||||||
glBindTexture(GL_TEXTURE_2D_ARRAY, self.texture_id)
|
glBindTexture(GL_TEXTURE_2D_ARRAY, self.texture_id)
|
||||||
|
|
||||||
glActiveTexture(self.buffer_texture_unit)
|
|
||||||
glBindTexture(GL_TEXTURE_BUFFER, self.buffer_texture_id)
|
|
||||||
|
|
||||||
def __exit__(self, *a):
|
def __exit__(self, *a):
|
||||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0)
|
glBindTexture(GL_TEXTURE_2D_ARRAY, 0)
|
||||||
glBindTexture(GL_TEXTURE_BUFFER, 0)
|
|
||||||
glBindBuffer(GL_TEXTURE_BUFFER, 0)
|
# }}}
|
||||||
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32UI, 0)
|
|
||||||
|
|
||||||
|
|
||||||
class ShaderProgram:
|
class ShaderProgram: # {{{
|
||||||
""" Helper class for using GLSL shader programs """
|
""" Helper class for using GLSL shader programs """
|
||||||
|
|
||||||
def __init__(self, vertex, fragment):
|
def __init__(self, vertex, fragment):
|
||||||
@@ -206,23 +248,43 @@ class ShaderProgram:
|
|||||||
raise ValueError('Error linking shader program: \n%s' % info.decode('utf-8'))
|
raise ValueError('Error linking shader program: \n%s' % info.decode('utf-8'))
|
||||||
glDeleteShader(vs_id)
|
glDeleteShader(vs_id)
|
||||||
glDeleteShader(frag_id)
|
glDeleteShader(frag_id)
|
||||||
self.vao_id = glGenVertexArrays(1)
|
self.vertex_arrays = {}
|
||||||
self.buffers = {}
|
|
||||||
|
|
||||||
def add_vertex_array(self, name, size=3, dtype=GL_FLOAT, normalized=False, stride=0, offset=0):
|
def vertex_array(self, name, size=3, dtype=GL_FLOAT, normalized=False, stride=0, offset=0, divisor=0):
|
||||||
glBindVertexArray(self.vao_id)
|
return VertexArray(name, size, dtype, normalized, stride, offset, divisor)
|
||||||
if name not in self.buffers:
|
|
||||||
self.buffers[name] = glGenBuffers(1)
|
def add_vertex_arrays(self, *arrays):
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, self.buffers[name])
|
vao_id = glGenVertexArrays(1)
|
||||||
aid = self.attribute_location(name)
|
self.vertex_arrays[vao_id] = buf_id = buffer_manager.create(for_use=GL_ARRAY_BUFFER)
|
||||||
glEnableVertexAttribArray(aid)
|
with self.bound_vertex_array(vao_id), buffer_manager.bound_buffer(buf_id):
|
||||||
glVertexAttribPointer(aid, size, dtype, normalized, stride, offset)
|
for x in arrays:
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
aid = self.attribute_location(x.name)
|
||||||
|
if aid > -1:
|
||||||
|
glEnableVertexAttribArray(aid)
|
||||||
|
glVertexAttribPointer(aid, x.size, x.dtype, x.normalized, x.stride, x.offset)
|
||||||
|
if x.divisor > 0:
|
||||||
|
glVertexAttribDivisor(aid, x.divisor)
|
||||||
|
return vao_id
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def bound_vertex_array(self, vao_id):
|
||||||
|
glBindVertexArray(vao_id)
|
||||||
|
yield
|
||||||
glBindVertexArray(0)
|
glBindVertexArray(0)
|
||||||
|
|
||||||
def send_vertex_data(self, name, data, usage=GL_STATIC_DRAW):
|
def remove_vertex_array(self, vao_id):
|
||||||
bufid = self.buffers[name]
|
buf_id = self.vertex_arrays.pop(vao_id, None)
|
||||||
glNamedBufferData(bufid, sizeof(data), addressof(data), usage)
|
if buf_id is not None:
|
||||||
|
glDeleteVertexArray(vao_id)
|
||||||
|
buffer_manager.delete(buf_id)
|
||||||
|
|
||||||
|
def send_vertex_data(self, vao_id, data, usage=GL_STREAM_DRAW):
|
||||||
|
bufid = self.vertex_arrays[vao_id]
|
||||||
|
buffer_manager.set_data(bufid, data, usage=usage)
|
||||||
|
|
||||||
|
def get_vertex_data(self, vao_id):
|
||||||
|
bufid = self.vertex_arrays[vao_id]
|
||||||
|
return buffer_manager.get_data(bufid)
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return self.program_id
|
return self.program_id
|
||||||
@@ -260,8 +322,7 @@ class ShaderProgram:
|
|||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
glUseProgram(self.program_id)
|
glUseProgram(self.program_id)
|
||||||
glBindVertexArray(self.vao_id)
|
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
glUseProgram(0)
|
glUseProgram(0)
|
||||||
glBindVertexArray(0)
|
# }}}
|
||||||
|
|||||||
@@ -3,18 +3,23 @@
|
|||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
from ctypes import addressof
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from ctypes import addressof
|
|
||||||
|
|
||||||
|
from .borders import Borders
|
||||||
|
from .char_grid import calculate_gl_geometry, render_cells
|
||||||
from .child import Child
|
from .child import Child
|
||||||
from .config import build_ansi_color_table
|
from .config import build_ansi_color_table
|
||||||
from .constants import get_boss, appname, shell_path, cell_size, queue_action, viewport_size, WindowGeometry, GLuint
|
from .constants import (
|
||||||
from .fast_data_types import glfw_post_empty_event, Screen, DECAWM, DATA_CELL_SIZE, ColorProfile
|
GLuint, WindowGeometry, appname, cell_size, get_boss, queue_action,
|
||||||
from .char_grid import calculate_gl_geometry, render_cells
|
shell_path, viewport_size
|
||||||
from .layout import all_layouts, Rect
|
)
|
||||||
|
from .fast_data_types import (
|
||||||
|
DATA_CELL_SIZE, DECAWM, ColorProfile, Screen, glfw_post_empty_event
|
||||||
|
)
|
||||||
|
from .layout import Rect, all_layouts
|
||||||
from .utils import color_as_int
|
from .utils import color_as_int
|
||||||
from .borders import Borders
|
|
||||||
from .window import Window
|
from .window import Window
|
||||||
|
|
||||||
|
|
||||||
@@ -32,9 +37,12 @@ class Tab:
|
|||||||
self.borders = Borders(opts)
|
self.borders = Borders(opts)
|
||||||
self.windows = deque()
|
self.windows = deque()
|
||||||
self.active_window_idx = 0
|
self.active_window_idx = 0
|
||||||
|
for i, which in enumerate('first second third fourth fifth sixth seventh eighth ninth tenth'.split()):
|
||||||
|
setattr(self, which + '_window', partial(self.nth_window, num=i))
|
||||||
if session_tab is None:
|
if session_tab is None:
|
||||||
self.cwd = args.directory
|
self.cwd = args.directory
|
||||||
l = self.enabled_layouts[0]
|
l = self.enabled_layouts[0]
|
||||||
|
self.current_layout = all_layouts[l](opts, self.borders.border_width, self.windows)
|
||||||
if special_window is None:
|
if special_window is None:
|
||||||
queue_action(self.new_window)
|
queue_action(self.new_window)
|
||||||
else:
|
else:
|
||||||
@@ -42,10 +50,8 @@ class Tab:
|
|||||||
else:
|
else:
|
||||||
self.cwd = session_tab.cwd or args.directory
|
self.cwd = session_tab.cwd or args.directory
|
||||||
l = session_tab.layout
|
l = session_tab.layout
|
||||||
|
self.current_layout = all_layouts[l](opts, self.borders.border_width, self.windows)
|
||||||
queue_action(self.startup, session_tab)
|
queue_action(self.startup, session_tab)
|
||||||
self.current_layout = all_layouts[l](opts, self.borders.border_width, self.windows)
|
|
||||||
for i, which in enumerate('first second third fourth fifth sixth seventh eighth ninth tenth'.split()):
|
|
||||||
setattr(self, which + '_window', partial(self.nth_window, num=i))
|
|
||||||
|
|
||||||
def startup(self, session_tab):
|
def startup(self, session_tab):
|
||||||
for cmd in session_tab.windows:
|
for cmd in session_tab.windows:
|
||||||
@@ -201,7 +207,7 @@ class TabManager:
|
|||||||
|
|
||||||
def __init__(self, opts, args, startup_session):
|
def __init__(self, opts, args, startup_session):
|
||||||
self.opts, self.args = opts, args
|
self.opts, self.args = opts, args
|
||||||
self.buffer_id = None
|
self.vao_id = None
|
||||||
self.tabbar_lock = Lock()
|
self.tabbar_lock = Lock()
|
||||||
self.tabs = [Tab(opts, args, self.title_changed, t) for t in startup_session.tabs]
|
self.tabs = [Tab(opts, args, self.title_changed, t) for t in startup_session.tabs]
|
||||||
self.cell_ranges = []
|
self.cell_ranges = []
|
||||||
@@ -305,7 +311,7 @@ class TabManager:
|
|||||||
if needs_resize:
|
if needs_resize:
|
||||||
queue_action(get_boss().tabbar_visibility_changed)
|
queue_action(get_boss().tabbar_visibility_changed)
|
||||||
|
|
||||||
def update_tab_bar_data(self, sprites):
|
def update_tab_bar_data(self, sprites, cell_program):
|
||||||
s = self.tab_bar_screen
|
s = self.tab_bar_screen
|
||||||
s.cursor.x = 0
|
s.cursor.x = 0
|
||||||
s.erase_in_line(2, False)
|
s.erase_in_line(2, False)
|
||||||
@@ -335,9 +341,9 @@ class TabManager:
|
|||||||
s.update_cell_data(
|
s.update_cell_data(
|
||||||
sprites.backend, self.color_profile, addressof(self.sprite_map), self.default_fg, self.default_bg, True)
|
sprites.backend, self.color_profile, addressof(self.sprite_map), self.default_fg, self.default_bg, True)
|
||||||
sprites.render_dirty_cells()
|
sprites.render_dirty_cells()
|
||||||
if self.buffer_id is None:
|
if self.vao_id is None:
|
||||||
self.buffer_id = sprites.add_sprite_map()
|
self.vao_id = cell_program.create_sprite_map()
|
||||||
sprites.set_sprite_map(self.buffer_id, self.sprite_map)
|
cell_program.send_vertex_data(self.vao_id, self.sprite_map)
|
||||||
|
|
||||||
def activate_tab_at(self, x):
|
def activate_tab_at(self, x):
|
||||||
x = (x - self.window_geometry.left) // cell_size.width
|
x = (x - self.window_geometry.left) // cell_size.width
|
||||||
@@ -357,5 +363,5 @@ class TabManager:
|
|||||||
return
|
return
|
||||||
with self.tabbar_lock:
|
with self.tabbar_lock:
|
||||||
if self.tabbar_dirty:
|
if self.tabbar_dirty:
|
||||||
self.update_tab_bar_data(sprites)
|
self.update_tab_bar_data(sprites, cell_program)
|
||||||
render_cells(self.buffer_id, self.screen_geometry, cell_program, sprites)
|
render_cells(self.vao_id, self.screen_geometry, cell_program, sprites)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from functools import lru_cache
|
|||||||
from time import monotonic
|
from time import monotonic
|
||||||
|
|
||||||
from .constants import isosx
|
from .constants import isosx
|
||||||
from .fast_data_types import glfw_get_physical_dpi, wcwidth as wcwidth_impl
|
from .fast_data_types import glfw_get_physical_dpi, wcwidth as wcwidth_impl, redirect_std_streams
|
||||||
from .rgb import Color, to_color
|
from .rgb import Color, to_color
|
||||||
|
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ def get_primary_selection():
|
|||||||
return '' # There is no primary selection on OS X
|
return '' # There is no primary selection on OS X
|
||||||
# glfw has no way to get the primary selection
|
# glfw has no way to get the primary selection
|
||||||
# https://github.com/glfw/glfw/issues/894
|
# https://github.com/glfw/glfw/issues/894
|
||||||
return subprocess.check_output(['xsel', '-p']).decode('utf-8')
|
return subprocess.check_output(['xsel', '-p'], stderr=open(os.devnull, 'wb'), stdin=open(os.devnull, 'rb')).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def base64_encode(
|
def base64_encode(
|
||||||
@@ -203,7 +203,7 @@ def set_primary_selection(text):
|
|||||||
return # There is no primary selection on OS X
|
return # There is no primary selection on OS X
|
||||||
if isinstance(text, str):
|
if isinstance(text, str):
|
||||||
text = text.encode('utf-8')
|
text = text.encode('utf-8')
|
||||||
p = subprocess.Popen(['xsel', '-i', '-p'], stdin=subprocess.PIPE)
|
p = subprocess.Popen(['xsel', '-i', '-p'], stdin=subprocess.PIPE, stdout=open(os.devnull, 'wb'), stderr=subprocess.STDOUT)
|
||||||
p.stdin.write(text), p.stdin.close()
|
p.stdin.write(text), p.stdin.close()
|
||||||
p.wait()
|
p.wait()
|
||||||
|
|
||||||
@@ -215,3 +215,14 @@ def open_url(url, program='default'):
|
|||||||
cmd = shlex.split(program)
|
cmd = shlex.split(program)
|
||||||
cmd.append(url)
|
cmd.append(url)
|
||||||
subprocess.Popen(cmd).wait()
|
subprocess.Popen(cmd).wait()
|
||||||
|
|
||||||
|
|
||||||
|
def detach(fork=True, setsid=True, redirect=True):
|
||||||
|
if fork:
|
||||||
|
# Detach from the controlling process.
|
||||||
|
if os.fork() != 0:
|
||||||
|
raise SystemExit(0)
|
||||||
|
if setsid:
|
||||||
|
os.setsid()
|
||||||
|
if redirect:
|
||||||
|
redirect_std_streams(os.devnull)
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -319,7 +319,7 @@ def package(args, for_bundle=False): # {{{
|
|||||||
def src_ignore(parent, entries):
|
def src_ignore(parent, entries):
|
||||||
return [
|
return [
|
||||||
x for x in entries
|
x for x in entries
|
||||||
if '.' in x and x.rpartition('.')[2] not in ('py', 'so', 'conf')
|
if '.' in x and x.rpartition('.')[2] not in ('py', 'so', 'conf', 'glsl')
|
||||||
]
|
]
|
||||||
|
|
||||||
shutil.copytree('kitty', os.path.join(libdir, 'kitty'), ignore=src_ignore)
|
shutil.copytree('kitty', os.path.join(libdir, 'kitty'), ignore=src_ignore)
|
||||||
|
|||||||
Reference in New Issue
Block a user