mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-06 01:05:48 +02:00
Start work on implementing the char grid
This commit is contained in:
@@ -23,6 +23,7 @@ class Boss(Thread):
|
||||
shutting_down = False
|
||||
pending_title_change = pending_icon_change = None
|
||||
pending_color_changes = {}
|
||||
pending_update_screen = None
|
||||
|
||||
def __init__(self, window, window_width, window_height, opts, args):
|
||||
Thread.__init__(self, name='ChildMonitor')
|
||||
@@ -36,16 +37,22 @@ class Boss(Thread):
|
||||
self.tracker = ChangeTracker(self.mark_dirtied)
|
||||
self.screen = Screen(self.opts, self.tracker, self)
|
||||
self.char_grid = CharGrid(self.screen, opts, window_width, window_height)
|
||||
self.queue_action(self.initialize)
|
||||
sclass = DebugStream if args.dump_commands else Stream
|
||||
self.stream = sclass(self.screen)
|
||||
self.write_buf = memoryview(b'')
|
||||
resize_pty(80, 24)
|
||||
|
||||
def on_window_resize(self, window, w, h):
|
||||
self.queue_action(self.resize_screen, w, h)
|
||||
def initialize(self):
|
||||
self.char_grid.initialize()
|
||||
glfw.glfwPostEmptyEvent()
|
||||
|
||||
def resize_screen(self, w, h):
|
||||
def on_window_resize(self, window, w, h):
|
||||
self.queue_action(self.apply_resize_screen, w, h)
|
||||
|
||||
def apply_resize_screen(self, w, h):
|
||||
self.char_grid.resize_screen(w, h)
|
||||
glfw.glfwPostEmptyEvent()
|
||||
|
||||
def apply_opts(self, opts):
|
||||
self.opts = opts
|
||||
@@ -65,10 +72,16 @@ class Boss(Thread):
|
||||
self.char_grid.render()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.loop.run_forever()
|
||||
finally:
|
||||
self.loop.close()
|
||||
self.loop.run_forever()
|
||||
|
||||
def close(self):
|
||||
if not self.shutting_down:
|
||||
self.queue_action(self.shutdown)
|
||||
|
||||
def destroy(self):
|
||||
# Must be called in the main thread
|
||||
self.loop.close()
|
||||
del self.loop
|
||||
|
||||
def shutdown(self):
|
||||
self.shutting_down = True
|
||||
@@ -114,11 +127,14 @@ class Boss(Thread):
|
||||
self.loop.add_writer(self.child_fd, self.write_ready)
|
||||
|
||||
def mark_dirtied(self):
|
||||
self.queue_action(self.update_screen)
|
||||
# Batch screen updates
|
||||
if self.pending_update_screen is None:
|
||||
self.pending_update_screen = self.loop.call_later(0.02, self.apply_update_screen)
|
||||
|
||||
def update_screen(self):
|
||||
def apply_update_screen(self):
|
||||
self.pending_update_screen = None
|
||||
changes = self.tracker.consolidate_changes()
|
||||
self.char_grid.update_screen(changes)
|
||||
self.char_grid.update_cell_data(changes)
|
||||
glfw.glfwPostEmptyEvent()
|
||||
|
||||
def title_changed(self, new_title):
|
||||
@@ -131,9 +147,9 @@ class Boss(Thread):
|
||||
|
||||
def change_default_color(self, which, value):
|
||||
self.pending_color_changes[which] = value
|
||||
self.queue_action(self.change_colors)
|
||||
self.queue_action(self.apply_change_colors)
|
||||
|
||||
def change_colors(self):
|
||||
def apply_change_colors(self):
|
||||
self.char_grid.change_colors(self.pending_color_changes)
|
||||
self.pending_color_changes = {}
|
||||
glfw.glfwPostEmptyEvent()
|
||||
|
||||
@@ -2,29 +2,122 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from threading import Lock
|
||||
from collections import namedtuple
|
||||
from copy import copy
|
||||
from itertools import chain, repeat
|
||||
from queue import Queue, Empty
|
||||
|
||||
from .config import build_ansi_color_tables, to_color
|
||||
from .config import build_ansi_color_tables, to_color, fg_color_table, bg_color_table
|
||||
from .data_types import COL_MASK, COL_SHIFT, ITALIC_MASK, BOLD_MASK, REVERSE_MASK, as_color
|
||||
from .fonts import set_font_family
|
||||
from .shaders import Sprites, ShaderProgram
|
||||
|
||||
import OpenGL.GL as gl
|
||||
|
||||
Size = namedtuple('Size', 'width height')
|
||||
ScreenGeometry = namedtuple('ScreenGeometry', 'xstart ystart xnum ynum dx dy')
|
||||
|
||||
# cell shader {{{
|
||||
|
||||
cell_shader = (
|
||||
'''\
|
||||
uniform uvec2 dimensions; // xnum, ynum
|
||||
uniform vec4 steps; // xstart, ystart, dx, dy
|
||||
uniform vec2 sprite_layout; // dx, dy
|
||||
uniform usamplerBuffer sprite_map; // gl_InstanceID -> x, y, z
|
||||
out vec3 sprite_pos;
|
||||
out vec4 foreground;
|
||||
out vec4 background;
|
||||
|
||||
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() {
|
||||
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 = int(instance_id) * 3;
|
||||
uvec4 spos = texelFetch(sprite_map, sprite_id);
|
||||
vec2 s_xpos = vec2(spos[0], spos[0] + 1.0) * sprite_layout[0];
|
||||
vec2 s_ypos = vec2(spos[1], spos[1] + 1.0) * sprite_layout[1];
|
||||
sprite_pos = vec3(s_xpos[pos[0]], s_ypos[pos[1]], spos[2]);
|
||||
foreground = texelFetch(sprite_map, sprite_id + 1) / 255.0;
|
||||
background = texelFetch(sprite_map, sprite_id + 2) / 255.0;
|
||||
}
|
||||
''',
|
||||
|
||||
'''\
|
||||
uniform sampler2DArray sprites;
|
||||
in vec3 sprite_pos;
|
||||
in vec4 foreground;
|
||||
in vec4 background;
|
||||
out vec4 final_color;
|
||||
|
||||
void main() {
|
||||
float alpha = texture(sprites, sprite_pos).r;
|
||||
final_color = background * (1 - alpha) + foreground * alpha;
|
||||
}
|
||||
''')
|
||||
# }}}
|
||||
|
||||
|
||||
def calculate_vertices(cell_width, cell_height, screen_width, screen_height):
|
||||
xnum = screen_width // cell_width
|
||||
ynum = screen_height // cell_height
|
||||
dx, dy = 2 * cell_width / screen_width, 2 * cell_height / screen_height
|
||||
xmargin = (screen_width - (xnum * cell_width)) / screen_width
|
||||
ymargin = (screen_height - (ynum * cell_height)) / screen_height
|
||||
xstart = -1 + xmargin
|
||||
ystart = 1 - ymargin
|
||||
return ScreenGeometry(xstart, ystart, xnum, ynum, dx, dy)
|
||||
|
||||
|
||||
class RenderData:
|
||||
|
||||
__slots__ = 'viewport clear_color cell_data screen_geometry sprite_layout'.split()
|
||||
|
||||
def __init__(self, viewport=None, clear_color=None, cell_data=None, screen_geometry=None, sprite_layout=None):
|
||||
self.viewport, self.clear_color, self.cell_data = viewport, clear_color, cell_data
|
||||
self.screen_geometry = screen_geometry
|
||||
self.sprite_layout = sprite_layout
|
||||
|
||||
def update(self, other):
|
||||
for k in self.__slots__:
|
||||
val = getattr(other, k)
|
||||
if val is not None:
|
||||
setattr(self, k, val)
|
||||
|
||||
empty_cell = (' ', 0)
|
||||
|
||||
|
||||
class CharGrid:
|
||||
|
||||
def __init__(self, screen, opts, window_width, window_height):
|
||||
self.width, self.height = window_width, window_height
|
||||
self.screen = screen
|
||||
self.apply_opts(opts)
|
||||
self.dirty_everything()
|
||||
self.default_bg, self.default_fg = self.original_bg, self.original_fg
|
||||
self.resize_lock = Lock()
|
||||
self.apply_resize_to_screen(self.width, self.height)
|
||||
self.opts = opts
|
||||
self.original_bg = opts.background
|
||||
self.original_fg = opts.foreground
|
||||
self.render_queue = Queue()
|
||||
self.program = ShaderProgram(*cell_shader)
|
||||
self.sprites = Sprites()
|
||||
self.last_render_data = RenderData()
|
||||
self.render_queue.put(RenderData(viewport=Size(self.width, self.height), clear_color=self.original_bg))
|
||||
|
||||
def dirty_everything(self):
|
||||
self.cell_resize_pending = True
|
||||
self.clear_color_changed = True
|
||||
self.resize_pending = self.width, self.height
|
||||
def initialize(self):
|
||||
self.apply_opts(self.opts)
|
||||
self.default_bg, self.default_fg = self.original_bg, self.original_fg
|
||||
|
||||
def apply_opts(self, opts):
|
||||
self.opts = opts
|
||||
@@ -33,36 +126,19 @@ class CharGrid:
|
||||
self.original_bg = opts.background
|
||||
self.original_fg = opts.foreground
|
||||
self.cell_width, self.cell_height = set_font_family(opts.font_family, opts.font_size)
|
||||
|
||||
def apply_resize_to_screen(self, w, h):
|
||||
cells_per_line = w // self.cell_width
|
||||
lines_per_screen = h // self.cell_height
|
||||
self.screen.resize(lines_per_screen, cells_per_line)
|
||||
self.do_layout(self.width, self.height)
|
||||
|
||||
def resize_screen(self, w, h):
|
||||
' Screen was resized by the user (called in non-UI thread) '
|
||||
with self.resize_lock:
|
||||
self.apply_resize_to_screen(w, h)
|
||||
self.resize_pending = w, h
|
||||
self.do_layout(w, h)
|
||||
|
||||
def do_layout(self, w, h):
|
||||
self.width, self.height = w, h
|
||||
self.cells_per_line = w // self.cell_width
|
||||
self.lines_per_screen = h // self.cell_height
|
||||
if self.cell_resize_pending:
|
||||
self.cell_resize_pending = False
|
||||
|
||||
def render(self):
|
||||
with self.resize_lock:
|
||||
if self.resize_pending:
|
||||
self.do_layout(*self.resize_pending)
|
||||
gl.glViewport(0, 0, self.width, self.height)
|
||||
self.resize_pending = None
|
||||
if self.clear_color_changed:
|
||||
bg = self.default_bg
|
||||
self.clear_color_changed = False
|
||||
gl.glClearColor(bg[0]/255, bg[1]/255, bg[2]/255, 1)
|
||||
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
||||
self.screen_geometry = sg = calculate_vertices(self.cell_width, self.cell_height, self.width, self.height)
|
||||
self.screen.resize(sg.ynum, sg.xnum)
|
||||
self.sprite_map = (gl.GLuint * (sg.ynum * sg.xnum * 9))()
|
||||
self.sprite_text = list(repeat(empty_cell, sg.xnum * sg.ynum))
|
||||
self.update_cell_data(add_viewport_data=True)
|
||||
|
||||
def change_colors(self, changes):
|
||||
dirtied = False
|
||||
@@ -77,9 +153,103 @@ class CharGrid:
|
||||
setattr(self, 'default_' + which, val)
|
||||
dirtied = True
|
||||
if dirtied:
|
||||
self.clear_color_changed = True
|
||||
self.update_screen()
|
||||
self.render_queue.put(RenderData(clear_color=self.default_bg))
|
||||
|
||||
def update_screen(self, changes=None):
|
||||
def update_cell_data(self, changes=None, add_viewport_data=False):
|
||||
rd = RenderData(sprite_layout=self.sprites.layout)
|
||||
if add_viewport_data:
|
||||
rd.viewport = Size(self.width, self.height)
|
||||
rd.screen_geometry = self.screen_geometry
|
||||
if changes is None:
|
||||
changes = {'screen': True}
|
||||
sg = self.screen_geometry
|
||||
cell_data_changed = changes['screen'] or changes['cells'] or changes['lines']
|
||||
if cell_data_changed:
|
||||
if changes['screen']:
|
||||
lines = range(sg.ynum)
|
||||
cell_ranges = {}
|
||||
else:
|
||||
lines = changes['lines']
|
||||
cell_ranges = changes['cells']
|
||||
|
||||
fgct = fg_color_table()
|
||||
bgct = bg_color_table()
|
||||
dfbg = self.default_bg
|
||||
dffg = self.default_fg
|
||||
|
||||
for y in lines:
|
||||
self.update_line(y, range(sg.xnum), fgct, bgct, dffg, dfbg)
|
||||
|
||||
for y, ranges in cell_ranges.items():
|
||||
self.update_line(y, chain.from_iterable(range(start, stop + 1) for start, stop in ranges),
|
||||
fgct, bgct, dffg, dfbg)
|
||||
|
||||
rd.cell_data = copy(self.sprite_map), self.sprite_text[:]
|
||||
rd.sprite_layout = self.sprites.layout
|
||||
self.render_queue.put(rd)
|
||||
|
||||
def update_line(self, y, cell_range, fgct, bgct, dffg, dfbg):
|
||||
line = self.screen.line(y)
|
||||
for x in cell_range:
|
||||
self.update_cell(line, x, y, fgct, bgct, dffg, dfbg)
|
||||
|
||||
def update_cell(self, line, x, y, fgct, bgct, dffg, dfbg):
|
||||
idx = x + y * self.screen_geometry.xnum
|
||||
offset = idx * 9
|
||||
ch, attrs, colors = line.basic_cell_data(x)
|
||||
bgcol = colors >> COL_SHIFT
|
||||
if bgcol:
|
||||
bgcol = as_color(bgcol, bgct) or dfbg
|
||||
else:
|
||||
bgcol = dfbg
|
||||
fgcol = colors & COL_MASK
|
||||
if fgcol:
|
||||
fgcol = as_color(fgcol, fgct) or dffg
|
||||
else:
|
||||
fgcol = dffg
|
||||
if attrs & REVERSE_MASK:
|
||||
self.sprite_map[offset + 3:offset + 6] = bgcol
|
||||
self.sprite_map[offset + 6:offset + 9] = fgcol
|
||||
else:
|
||||
self.sprite_map[offset + 3:offset + 6] = fgcol
|
||||
self.sprite_map[offset + 6:offset + 9] = bgcol
|
||||
if ch == 0 or ch == 32:
|
||||
self.sprite_text[idx] = empty_cell
|
||||
else:
|
||||
self.sprite_text[idx] = line.text_at(x), attrs
|
||||
|
||||
def render(self):
|
||||
' This is the only method in this class called in the UI thread (apart from __init__) '
|
||||
cell_data_changed = False
|
||||
while True:
|
||||
try:
|
||||
rd = self.render_queue.get_nowait()
|
||||
except Empty:
|
||||
break
|
||||
cell_data_changed |= rd.cell_data is not None
|
||||
if rd.clear_color is not None:
|
||||
bg = rd.clear_color
|
||||
gl.glClearColor(bg[0] / 255, bg[1] / 255, bg[2] / 255, 1)
|
||||
if rd.viewport is not None:
|
||||
gl.glViewport(0, 0, self.width, self.height)
|
||||
self.last_render_data.update(rd)
|
||||
if cell_data_changed:
|
||||
spmap, sptext = rd.cell_data
|
||||
for i, (text, attrs) in enumerate(sptext):
|
||||
f = i * 9
|
||||
spmap[f:f + 3] = self.sprites.primary_sprite_position(text, attrs & BOLD_MASK, attrs & ITALIC_MASK)
|
||||
self.sprites.set_sprite_map(spmap)
|
||||
|
||||
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
||||
if self.last_render_data.screen_geometry is None:
|
||||
return
|
||||
with self.program:
|
||||
ul = self.program.uniform_location
|
||||
sg = self.last_render_data.screen_geometry
|
||||
gl.glUniform2ui(ul('dimensions'), sg.xnum, sg.ynum)
|
||||
gl.glUniform4f(ul('steps'), sg.xstart, sg.ystart, sg.dx, sg.dy)
|
||||
gl.glUniform1i(ul('sprites'), self.sprites.sampler_num)
|
||||
gl.glUniform1i(ul('sprite_map'), self.sprites.buffer_sampler_num)
|
||||
gl.glUniform2f(ul('sprite_layout'), *self.last_render_data.sprite_layout)
|
||||
with self.sprites:
|
||||
gl.glDrawArraysInstanced(gl.GL_TRIANGLE_FAN, 0, 4, sg.xnum * sg.ynum)
|
||||
|
||||
@@ -8,57 +8,7 @@ import sys
|
||||
|
||||
from kitty.shaders import ShaderProgram, GL_VERSION, Sprites, check_for_required_extensions
|
||||
from kitty.fonts import set_font_family, cell_size
|
||||
|
||||
textured_shaders = (
|
||||
'''\
|
||||
uniform uvec2 dimensions; // xnum, ynum
|
||||
uniform vec4 steps; // xstart, ystart, dx, dy
|
||||
uniform vec2 sprite_layout; // dx, dy
|
||||
uniform usamplerBuffer sprite_map; // gl_InstanceID -> x, y, z
|
||||
out vec3 sprite_pos;
|
||||
out vec4 foreground;
|
||||
out vec4 background;
|
||||
|
||||
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() {
|
||||
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 = int(instance_id) * 3;
|
||||
uvec4 spos = texelFetch(sprite_map, sprite_id);
|
||||
vec2 s_xpos = vec2(spos[0], spos[0] + 1.0) * sprite_layout[0];
|
||||
vec2 s_ypos = vec2(spos[1], spos[1] + 1.0) * sprite_layout[1];
|
||||
sprite_pos = vec3(s_xpos[pos[0]], s_ypos[pos[1]], spos[2]);
|
||||
foreground = texelFetch(sprite_map, sprite_id + 1) / 255.0;
|
||||
background = texelFetch(sprite_map, sprite_id + 2) / 255.0;
|
||||
}
|
||||
''',
|
||||
|
||||
'''\
|
||||
uniform sampler2DArray sprites;
|
||||
in vec3 sprite_pos;
|
||||
in vec4 foreground;
|
||||
in vec4 background;
|
||||
out vec4 final_color;
|
||||
|
||||
void main() {
|
||||
float alpha = texture(sprites, sprite_pos).r;
|
||||
final_color = background * (1 - alpha) + foreground * alpha;
|
||||
}
|
||||
''')
|
||||
from kitty.char_grid import calculate_vertices, cell_shader
|
||||
|
||||
|
||||
def rectangle_uv(left=0, top=0, right=1, bottom=1):
|
||||
@@ -72,17 +22,6 @@ def rectangle_uv(left=0, top=0, right=1, bottom=1):
|
||||
)
|
||||
|
||||
|
||||
def calculate_vertices(cell_width, cell_height, screen_width, screen_height):
|
||||
xnum = screen_width // cell_width
|
||||
ynum = screen_height // cell_height
|
||||
dx, dy = 2 * cell_width / screen_width, 2 * cell_height / screen_height
|
||||
xmargin = (screen_width - (xnum * cell_width)) / screen_width
|
||||
ymargin = (screen_height - (ynum * cell_height)) / screen_height
|
||||
xstart = -1 + xmargin
|
||||
ystart = 1 - ymargin
|
||||
return xnum, ynum, xstart, ystart, dx, dy
|
||||
|
||||
|
||||
class Renderer:
|
||||
|
||||
def __init__(self, w, h):
|
||||
@@ -92,7 +31,7 @@ class Renderer:
|
||||
((0, 0, 0), (255, 255, 255)),
|
||||
((255, 255, 0), (0, 0, 255)),
|
||||
]
|
||||
self.program = ShaderProgram(*textured_shaders)
|
||||
self.program = ShaderProgram(*cell_shader)
|
||||
self.sprites = Sprites()
|
||||
self.do_layout()
|
||||
|
||||
@@ -104,8 +43,8 @@ class Renderer:
|
||||
def do_layout(self):
|
||||
# Divide into cells
|
||||
cell_width, cell_height = cell_size()
|
||||
self.xnum, self.ynum, self.xstart, self.ystart, self.dx, self.dy = calculate_vertices(cell_width, cell_height, self.w, self.h)
|
||||
data = (gl.GLuint * (self.xnum * self.ynum * 9))()
|
||||
self.screen_geometry = sg = calculate_vertices(cell_width, cell_height, self.w, self.h)
|
||||
data = (gl.GLuint * (sg.xnum * sg.ynum * 9))()
|
||||
for i in range(0, len(data), 9):
|
||||
idx = i // 9
|
||||
c = '%d' % (idx % 10)
|
||||
@@ -117,13 +56,14 @@ class Renderer:
|
||||
def render(self):
|
||||
with self.program:
|
||||
ul = self.program.uniform_location
|
||||
gl.glUniform2ui(ul('dimensions'), self.xnum, self.ynum)
|
||||
gl.glUniform4f(ul('steps'), self.xstart, self.ystart, self.dx, self.dy)
|
||||
sg = self.screen_geometry
|
||||
gl.glUniform2ui(ul('dimensions'), sg.xnum, sg.ynum)
|
||||
gl.glUniform4f(ul('steps'), sg.xstart, sg.ystart, sg.dx, sg.dy)
|
||||
gl.glUniform1i(ul('sprites'), self.sprites.sampler_num)
|
||||
gl.glUniform1i(ul('sprite_map'), self.sprites.buffer_sampler_num)
|
||||
gl.glUniform2f(ul('sprite_layout'), *self.sprites.layout)
|
||||
with self.sprites:
|
||||
gl.glDrawArraysInstanced(gl.GL_TRIANGLE_FAN, 0, 4, self.xnum * self.ynum)
|
||||
gl.glDrawArraysInstanced(gl.GL_TRIANGLE_FAN, 0, 4, sg.xnum * sg.ynum)
|
||||
|
||||
# window setup {{{
|
||||
|
||||
|
||||
@@ -57,11 +57,16 @@ def run_app(opts, args):
|
||||
boss = Boss(window, window_width, window_height, opts, args)
|
||||
glfw.glfwSetFramebufferSizeCallback(window, boss.on_window_resize)
|
||||
boss.start()
|
||||
|
||||
while not glfw.glfwWindowShouldClose(window):
|
||||
boss.render()
|
||||
glfw.glfwSwapBuffers(window)
|
||||
glfw.glfwWaitEvents()
|
||||
try:
|
||||
while not glfw.glfwWindowShouldClose(window):
|
||||
boss.render()
|
||||
glfw.glfwSwapBuffers(window)
|
||||
glfw.glfwWaitEvents()
|
||||
finally:
|
||||
if boss.is_alive():
|
||||
boss.close()
|
||||
boss.join()
|
||||
boss.destroy()
|
||||
finally:
|
||||
glfw.glfwDestroyWindow(window)
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ class Sprites:
|
||||
# extensions one they become available.
|
||||
|
||||
def __init__(self, texture_unit=0):
|
||||
self.xnum = self.ynum = 1
|
||||
self.sampler_num = texture_unit
|
||||
self.buffer_sampler_num = texture_unit + 1
|
||||
self.first_cell_cache = {}
|
||||
@@ -53,8 +54,8 @@ class Sprites:
|
||||
self.max_array_len = gl.glGetIntegerv(gl.GL_MAX_ARRAY_TEXTURE_LAYERS)
|
||||
self.max_texture_size = gl.glGetIntegerv(gl.GL_MAX_TEXTURE_SIZE)
|
||||
self.cell_width, self.cell_height = cell_size()
|
||||
self.xnum = self.max_texture_size // self.cell_width
|
||||
self.max_y = self.max_texture_size // self.cell_height
|
||||
self.xnum = max(1, self.max_texture_size // self.cell_width)
|
||||
self.max_y = max(1, self.max_texture_size // self.cell_height)
|
||||
self.ynum = 1
|
||||
|
||||
@property
|
||||
|
||||
Reference in New Issue
Block a user