def start(self): ''' _orig_ESCDELAY = '' try: _orig_ESCDELAY = os.environ['ESCDELAY'] except KeyError: pass os.environ['ESCDELAY'] = str(0) # Stop escape key from pausing game ''' self.screen = curses.initscr() self.has_colors = curses.has_colors() if self.has_colors: curses.start_color() curses.noecho() # Don't display kb-input curses.cbreak( ) # Send characters immediately to getch() instead of storing them in buffer. curses.nonl() # Allow to detect Enter-key press self.screen.keypad(True) # Allow curses.KEY_LEFT/KEY_RIGHT/... keys self.hide_cursor() # Hide cursor self.set_keyboard_delay(0) # By default don't block keyboard self.screen_height, self.screen_width = self.screen.getmaxyx() # init color attrs default_attr = curses.color_pair(0) default_pair = curses.pair_number(default_attr) self.fore_color, self.back_color = curses.pair_content(default_pair) self.color_attrs = dict( ) # dict{ (fg_color, bg_color) -> curses attr } self.color_attrs[(self.fore_color, self.back_color)] = default_attr self.next_color_pair = 1
def colorstr_from_attr(attr): colorpair = curses.pair_number(attr) fg, bg = curses.pair_content(colorpair) r = f'{fg} on {bg}' if attr & curses.A_BOLD: r += ' bold' if attr & curses.A_UNDERLINE: r += ' underline' return r
def main(): scr = None try: scr = curses.initscr() mgr = Manager(scr) if curses.has_colors() and curses.can_change_color(): curses.start_color() pair_number = curses.pair_number( curses.color_pair(curses.COLOR_BLUE)) curses.init_pair(pair_number, curses.COLOR_WHITE, curses.COLOR_BLUE) curses.color_content(curses.COLOR_RED) scr.bkgdset(curses.COLOR_BLUE) curses.def_prog_mode() curses.noecho() curses.cbreak() curses.curs_set(0) scr.keypad(1) mgr.run() clean(scr=scr) except KeyboardInterrupt: clean(scr=scr) except: clean(scr=scr)
class ColorSheet(Sheet): rowtype = 'colors' # rowdef: color number as assigned in the colors object columns = [ Column('color', type=int), Column('R', getter=lambda col, row: curses.color_content( curses.pair_number(colors[row]) - 1)[0]), Column('G', getter=lambda col, row: curses.color_content( curses.pair_number(colors[row]) - 1)[1]), Column('B', getter=lambda col, row: curses.color_content( curses.pair_number(colors[row]) - 1)[2]), ] colorizers = [Colorizer('row', 7, lambda s, c, r, v: r)] def reload(self): self.rows = sorted(colors.keys(), key=lambda n: wrapply(int, n))
def setpxcb(xy, c, f, e): if xy[0] >= 0 and xy[1] >= 0 and xy[0] < size[1] and xy[1] < size[0] - (MG_TOP + MG_BOTTOM): scrmap[xy[0]][xy[1]]['e'] = e cpn = curses.pair_number(scrmap[xy[0]][xy[1]].get('a', 0)) f = f.format(fg = cpn % 8, bg = cpn // 8) if f in eval_cache: fp = eval_cache[f] else: eval_cache[f] = eval(f) fp = eval_cache[f] scrmap[xy[0]][xy[1]]['a'] = fp stdscr.addstr(xy[1]+MG_TOP, xy[0], c.encode(encoding), fp)
def create_color_pair(session, window, color_set_name, color_set) -> int: ''' Create a new curses color pair, store its associated name in session. Receives color_set dict of fg/bg color names or curses color numbers. Return color pair number. ''' i = len(session['colors']) # In case color_set does not define fg or bg, send current color set # to be merged over the missing values. current = curses.pair_content(curses.pair_number(window.getbkgd() & \ curses.A_COLOR)) fg = color_set.get('fg', None) bg = color_set.get('bg', None) set_color_pair(i, fg, bg, merge_with=current) session['colors'][color_set_name] = i return i
def __leaf_08(ql: Qiling): stdscr = ql.os.stdscr if stdscr is None: ql.reg.ax = 0x0720 else: cy, cx = stdscr.getyx() inch = stdscr.inch(cy, cx) attr = inch & curses.A_COLOR ch = inch & 0xFF ql.reg.al = ch pair_number = curses.pair_number(attr) fg, bg = curses.pair_content(pair_number) orig_fg = REVERSE_COLORS_MAPPING[fg] orig_bg = REVERSE_COLORS_MAPPING[bg] if attr & curses.A_BLINK: orig_bg |= 0b1000 ql.reg.ah = ((orig_bg << 4) & orig_fg)
def current_fg(self): colorpair = curses.pair_number(colors.get(self.current_color)) fg, bg = curses.pair_content(colorpair) return f'{fg}'
def module_funcs(stdscr): "Test module-level functions" for func in [curses.baudrate, curses.beep, curses.can_change_color, curses.cbreak, curses.def_prog_mode, curses.doupdate, curses.filter, curses.flash, curses.flushinp, curses.has_colors, curses.has_ic, curses.has_il, curses.isendwin, curses.killchar, curses.longname, curses.nocbreak, curses.noecho, curses.nonl, curses.noqiflush, curses.noraw, curses.reset_prog_mode, curses.termattrs, curses.termname, curses.erasechar, curses.getsyx]: func() # Functions that actually need arguments if curses.tigetstr("cnorm"): curses.curs_set(1) curses.delay_output(1) curses.echo() ; curses.echo(1) f = tempfile.TemporaryFile() stdscr.putwin(f) f.seek(0) curses.getwin(f) f.close() curses.halfdelay(1) curses.intrflush(1) curses.meta(1) curses.napms(100) curses.newpad(50,50) win = curses.newwin(5,5) win = curses.newwin(5,5, 1,1) curses.nl() ; curses.nl(1) curses.putp('abc') curses.qiflush() curses.raw() ; curses.raw(1) curses.setsyx(5,5) curses.tigetflag('hc') curses.tigetnum('co') curses.tigetstr('cr') curses.tparm('cr') curses.typeahead(sys.__stdin__.fileno()) curses.unctrl('a') curses.ungetch('a') curses.use_env(1) # Functions only available on a few platforms if curses.has_colors(): curses.start_color() curses.init_pair(2, 1,1) curses.color_content(1) curses.color_pair(2) curses.pair_content(curses.COLOR_PAIRS - 1) curses.pair_number(0) if hasattr(curses, 'use_default_colors'): curses.use_default_colors() if hasattr(curses, 'keyname'): curses.keyname(13) if hasattr(curses, 'has_key'): curses.has_key(13) if hasattr(curses, 'getmouse'): (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED) # availmask indicates that mouse stuff not available. if availmask != 0: curses.mouseinterval(10) # just verify these don't cause errors m = curses.getmouse() curses.ungetmouse(*m)
def int10(self): # BIOS video support # https://en.wikipedia.org/wiki/INT_10H # https://stanislavs.org/helppc/idx_interrupt.html # implemented by curses ah = self.ql.reg.ah al = self.ql.reg.al if ah==0: # time to set up curses # copied from curses.wrapper self.stdscr = curses.initscr() curses.noecho() curses.cbreak() self.stdscr.keypad(1) try: curses.start_color() except: pass if al == 0 or al == 1: curses.resizeterm(25, 40) elif al == 2 or al == 3: curses.resizeterm(25, 80) elif al == 4 or al == 5 or al == 9 or al == 0xD or al == 0x13: curses.resizeterm(200, 320) elif al == 6: curses.resizeterm(200, 640) elif al == 8: curses.resizeterm(200, 160) elif al == 0xA or al == 0xE: curses.resizeterm(200, 640) elif al == 0xF: curses.resizeterm(350, 640) elif al == 0x10: curses.resizeterm(350, 640) elif al == 0x11 or al == 0x12: curses.resizeterm(480, 640) else: self.ql.nprint("Exception: int 10h syscall Not Found, al: %s" % hex(al)) raise NotImplementedError() # Quoted from https://linux.die.net/man/3/resizeterm # # If ncurses is configured to supply its own SIGWINCH handler, # the resizeterm function ungetch's a KEY_RESIZE which will be # read on the next call to getch. ch = self._get_ch_non_blocking() if ch == curses.KEY_RESIZE: self.ql.nprint(f"[!] You term has been resized!") elif ch != -1: curses.ungetch(ch) self.stdscr.scrollok(True) if not curses.has_colors(): self.ql.nprint(f"[!] Warning: your terminal doesn't support colors, content might not be displayed correctly.") # https://en.wikipedia.org/wiki/BIOS_color_attributes # blink support? if curses.has_colors(): for fg in range(16): for bg in range(16): color_pair_index = 16*fg + bg + 1 if fg not in self.color_pairs: self.color_pairs[fg] = {} curses.init_pair(color_pair_index, COLORS_MAPPING[fg], COLORS_MAPPING[bg]) color_pair = curses.color_pair(color_pair_index) self.color_pairs[fg][bg] = color_pair self.revese_color_pairs[color_pair] = (fg, bg) elif ah == 1: # limited support ch = self.ql.reg.ch if (ch & 0x20) != 0: curses.curs_set(0) elif ah == 2: # page number ignored dh = self.ql.reg.dh # row dl = self.ql.reg.dl # column self.stdscr.move(dh, dl) elif ah == 5: # No idea how to implement, do nothing here. self.ql.reg.al = 0 pass elif ah == 6: al = self.ql.reg.al # lines to scroll ch = self.ql.reg.ch # row of upper-left cornner cl = self.ql.reg.cl # column of upper-left corner dh = self.ql.reg.dh # row of lower right corner dl = self.ql.reg.dl # column of lower righ corner bh = self.ql.reg.bh fg = bh & 0xF bg = (bh & 0xF0) >> 4 y, x = self.stdscr.getmaxyx() cy, cx = self.stdscr.getyx() attr = self._get_attr(fg, bg) if ch != 0 or cl != 0 or dh != y - 1 or dl != x - 1: self.ql.nprint(f"[!] Warning: Partial scroll is unsupported. Will scroll the whole page.") self.ql.nprint(f"[!] Resolution: {y}x{x} but asked to scroll [({ch},{cl}),({dh}, {dl})]") if al != 0: self.stdscr.scroll(al) ny = 0 if cy - al < 0: ny = 0 else: ny = cy - al + 1 if al > y: al = y for ln in range(al): self.stdscr.addstr(ny + ln, 0, " "*x, attr) self.stdscr.move(cy, cx) else: self.stdscr.clear() # Alternate way? #for ln in range(y): # self.stdscr.addstr(ln, 0, " "*x, attr) self.stdscr.bkgd(" ", attr) self.stdscr.move(0, 0) elif ah == 8: if self.stdscr is None: self.ql.reg.ax = 0x0720 else: cy, cx = self.stdscr.getyx() inch = self.stdscr.inch(cy, cx) attr = inch & curses.A_COLOR ch = inch & 0xFF self.ql.reg.al = ch pair_number = curses.pair_number(attr) fg, bg = curses.pair_content(pair_number) orig_fg = REVERSE_COLORS_MAPPING[fg] orig_bg = REVERSE_COLORS_MAPPING[bg] if attr & curses.A_BLINK != 0: orig_bg |= 0b1000 self.ql.reg.ah = ((orig_bg << 4) & orig_fg) elif ah == 0xE: self.ql.dprint(0, f"Echo: {hex(al)} -> {curses.ascii.unctrl(al)}") y, x = self.stdscr.getmaxyx() cy, cx = self.stdscr.getyx() fg = self.ql.reg.bl # https://stackoverflow.com/questions/27674158/how-to-get-color-information-with-mvinch # https://linux.die.net/man/3/inch # https://github.com/mirror/ncurses/blob/master/include/curses.h.in#L1197 # wtf curses... attr = self.stdscr.inch(cy, cx) & curses.A_COLOR if al == 0xa: # \n will erase current line with echochar, so we have to handle it carefully. self.ql.nprint(f"Resolution: {x}x{y}, Cursor position: {cx},{cy}, Going to get a new line.") if y-1 == cy: # scroll doesn't affect our cursor self.stdscr.scroll(1) self.stdscr.move(cy, 0) else: self.stdscr.move(cy+1, 0) else: self.stdscr.echochar(al, attr) else: self.ql.nprint("Exception: int 10h syscall Not Found, ah: %s" % hex(ah)) raise NotImplementedError() if self.stdscr is not None: self.stdscr.refresh()
def __init__(self, colour): self.colour = colour fg_enum, bg_enum = curses.pair_content(curses.pair_number(self.colour)) self.fg = Default_colour(fg_enum) self.bg = Default_colour(bg_enum)
def reader(stdscr, ebook, index, width, y, pctg): k = 0 if SEARCHPATTERN is None else ord("/") rows, cols = stdscr.getmaxyx() x = (cols - width) // 2 contents = ebook.contents toc_src = ebook.toc_entries chpath = contents[index] content = ebook.file.open(chpath).read() content = content.decode("utf-8") parser = HTMLtoLines() try: parser.feed(content) parser.close() except: pass src_lines, imgs = parser.get_lines(width) totlines = len(src_lines) if y < 0 and totlines <= rows: y = 0 elif pctg is not None: y = round(pctg*totlines) else: y = y % totlines pad = curses.newpad(totlines, width + 2) # + 2 unnecessary if COLORSUPPORT: pad.bkgd(stdscr.getbkgd()) pad.keypad(True) for n, i in enumerate(src_lines): if re.search("\[IMG:[0-9]+\]", i): pad.addstr(n, width//2 - len(i)//2, i, curses.A_REVERSE) else: pad.addstr(n, 0, i) if index == 0: suff = " End --> " elif index == len(contents) - 1: suff = " <-- End " else: suff = " <-- End --> " # try except to be more flexible on terminal resize try: pad.addstr(n, width//2 - 7, suff, curses.A_REVERSE) except curses.error: pass stdscr.clear() stdscr.refresh() # try except to be more flexible on terminal resize try: pad.refresh(y,0, 0,x, rows-1,x+width) except curses.error: pass countstring = "" svline = "dontsave" while True: if countstring == "": count = 1 else: count = int(countstring) if k in range(48, 58): # i.e., k is a numeral countstring = countstring + chr(k) else: if k in QUIT: if k == 27 and countstring != "": countstring = "" else: savestate(ebook.path, index, width, y, y/totlines) sys.exit() elif k in SCROLL_UP: if count > 1: svline = y - 1 if y >= count: y -= count elif y == 0 and index != 0: return -1, width, -rows, None else: y = 0 elif k in PAGE_UP: if y == 0 and index != 0: return -1, width, -rows, None else: y = pgup(y, rows, LINEPRSRV, count) elif k in SCROLL_DOWN: if count > 1: svline = y + rows - 1 if y + count <= totlines - rows: y += count elif y == totlines - rows and index != len(contents)-1: return 1, width, 0, None else: y = totlines - rows elif k in PAGE_DOWN: if totlines - y - LINEPRSRV > rows: # y = pgdn(y, totlines, rows, LINEPRSRV, count) y += rows - LINEPRSRV elif index != len(contents)-1: return 1, width, 0, None elif k in HALF_UP: countstring = str(rows//2) k = list(SCROLL_UP)[0] continue elif k in HALF_DOWN: countstring = str(rows//2) k = list(SCROLL_DOWN)[0] continue elif k in CH_NEXT: if index + count < len(contents) - 1: return count, width, 0, None if index + count >= len(contents) - 1: return len(contents) - index - 1, width, 0, None elif k in CH_PREV: if index - count > 0: return -count, width, 0, None elif index - count <= 0: return -index, width, 0, None elif k in CH_HOME: y = 0 elif k in CH_END: y = pgend(totlines, rows) elif k in TOC: fllwd = toc(stdscr, toc_src, index) if fllwd is not None: if fllwd in {curses.KEY_RESIZE}|HELP|META: k = fllwd continue return fllwd - index, width, 0, None elif k in META: k = meta(stdscr, ebook) if k in {curses.KEY_RESIZE}|HELP|TOC: continue elif k in HELP: k = help(stdscr) if k in {curses.KEY_RESIZE}|META|TOC: continue elif k == WIDEN and (width + count) < cols - 4: width += count return 0, width, 0, y/totlines elif k == SHRINK: width -= count if width < 20: width = 20 return 0, width, 0, y/totlines elif k == WIDTH: if countstring == "": # if called without a count, toggle between 80 cols and full width if width != 80 and cols - 4 >= 80: return 0, 80, 0, y/totlines else: return 0, cols - 4, 0, y/totlines else: width = count if width < 20: width = 20 elif width >= cols - 4: width = cols - 4 return 0, width, 0, y/totlines # elif k == ord("0"): # if width != 80 and cols - 2 >= 80: # return 0, 80, 0, y/totlines # else: # return 0, cols - 2, 0, y/totlines elif k == ord("/"): ks, idxs = searching(stdscr, pad, src_lines, width, y, index, len(contents)) if ks in {curses.KEY_RESIZE, ord("/")}: k = ks continue elif SEARCHPATTERN is not None: return idxs, width, 0, None elif idxs is not None: y = idxs elif k == ord("o") and VWR is not None: gambar, idx = [], [] for n, i in enumerate(src_lines[y:y+rows]): img = re.search("(?<=\[IMG:)[0-9]+(?=\])", i) if img is not None: gambar.append(img.group()) idx.append(n) impath = "" if len(gambar) == 1: impath = imgs[int(gambar[0])] elif len(gambar) > 1: p, i = 0, 0 while p not in QUIT and p not in FOLLOW: stdscr.move(idx[i], x + width//2 + len(gambar[i]) + 1) stdscr.refresh() curses.curs_set(1) p = pad.getch() if p in SCROLL_DOWN: i += 1 elif p in SCROLL_UP: i -= 1 i = i % len(gambar) curses.curs_set(0) if p in FOLLOW: impath = imgs[int(gambar[i])] if impath != "": imgsrc = dots_path(chpath, impath) k = open_media(pad, ebook, imgsrc) continue elif k == MARKPOS: jumnum = pad.getch() if jumnum in range(49, 58): JUMPLIST[chr(jumnum)] = [index, width, y, y/totlines] else: k = jumnum continue elif k == JUMPTOPOS: jumnum = pad.getch() if jumnum in range(49, 58) and chr(jumnum) in JUMPLIST.keys(): tojumpidxdiff = JUMPLIST[chr(jumnum)][0]-index tojumpy = JUMPLIST[chr(jumnum)][2] tojumpctg = None if JUMPLIST[chr(jumnum)][1] == width else JUMPLIST[chr(jumnum)][3] return tojumpidxdiff, width, tojumpy, tojumpctg else: k = jumnum continue elif k == COLORSWITCH and COLORSUPPORT and countstring in {"", "0", "1", "2"}: if countstring == "": count_color = curses.pair_number(stdscr.getbkgd()) if count_color not in {2, 3}: count_color = 1 count_color = count_color % 3 else: count_color = count stdscr.bkgd(curses.color_pair(count_color+1)) return 0, width, y, None elif k == curses.KEY_RESIZE: savestate(ebook.path, index, width, y, y/totlines) # stated in pypi windows-curses page: # to call resize_term right after KEY_RESIZE if sys.platform == "win32": curses.resize_term(rows, cols) rows, cols = stdscr.getmaxyx() else: rows, cols = stdscr.getmaxyx() curses.resize_term(rows, cols) if cols < 22 or rows < 12: sys.exit("ERR: Screen was too small (min 22cols x 12rows).") if cols <= width + 4: return 0, cols - 4, 0, y/totlines else: return 0, width, y, None countstring = "" if svline != "dontsave": pad.chgat(svline, 0, width, curses.A_UNDERLINE) try: stdscr.clear() stdscr.addstr(0, 0, countstring) stdscr.refresh() if totlines - y < rows: pad.refresh(y,0, 0,x, totlines-y,x+width) else: pad.refresh(y,0, 0,x, rows-1,x+width) except curses.error: pass k = pad.getch() if svline != "dontsave": pad.chgat(svline, 0, width, curses.A_NORMAL) svline = "dontsave"
#
def reader(ebook, index, width, y, pctg, sect): global SHOWPROGRESS k = 0 if SEARCHPATTERN is None else ord("/") rows, cols = SCREEN.getmaxyx() x = (cols - width) // 2 contents = ebook.contents toc_name = ebook.toc_entries[0] toc_idx = ebook.toc_entries[1] toc_sect = ebook.toc_entries[2] toc_secid = {} chpath = contents[index] content = ebook.file.open(chpath).read() content = content.decode("utf-8") parser = HTMLtoLines(set(toc_sect)) # parser = HTMLtoLines() # try: parser.feed(content) parser.close() # except: # pass src_lines, imgs, toc_secid = parser.get_lines(width) totlines = len(src_lines) + 1 # 1 extra line for suffix if y < 0 and totlines <= rows: y = 0 elif pctg is not None: y = round(pctg * totlines) else: y = y % totlines pad = curses.newpad(totlines, width + 2) # + 2 unnecessary if COLORSUPPORT: pad.bkgd(SCREEN.getbkgd()) pad.keypad(True) LOCALPCTG = [] for n, i in enumerate(src_lines): if re.search("\\[IMG:[0-9]+\\]", i): pad.addstr(n, width // 2 - len(i) // 2 + 1, i, curses.A_REVERSE) else: pad.addstr(n, 0, i) if CFG["EnableProgressIndicator"]: LOCALPCTG.append(len(re.sub("\s", "", i))) # chapter suffix ch_suffix = "***" # "\u3064\u3065\u304f" つづく try: pad.addstr(n + 1, (width - len(ch_suffix)) // 2 + 1, ch_suffix) except curses.error: pass if CFG["EnableProgressIndicator"]: TOTALPCTG = sum(PERCENTAGE) TOTALLOCALPCTG = sum(PERCENTAGE[:index]) SCREEN.clear() SCREEN.refresh() # try except to be more flexible on terminal resize try: pad.refresh(y, 0, 0, x, rows - 1, x + width) except curses.error: pass if sect != "": y = toc_secid.get(sect, 0) countstring = "" try: while True: if countstring == "": count = 1 else: count = int(countstring) if k in range(48, 58): # i.e., k is a numeral countstring = countstring + chr(k) else: if k in K["Quit"]: if k == 27 and countstring != "": countstring = "" else: savestate(ebook.path, index, width, y, y / totlines) sys.exit() elif k in K["ScrollUp"]: if y >= count: y -= count elif index != 0: return -1, width, -rows, None, "" elif k in K["PageUp"]: if y == 0 and index != 0: return -1, width, -rows, None, "" else: y = pgup(y, rows, LINEPRSRV, count) elif k in K["ScrollDown"]: if y + count <= totlines - rows: y += count elif index != len(contents) - 1: return 1, width, 0, None, "" elif k in K["PageDown"]: if totlines - y - LINEPRSRV > rows: y += rows - LINEPRSRV # SCREEN.clear() # SCREEN.refresh() elif index != len(contents) - 1: return 1, width, 0, None, "" elif k in K["NextChapter"]: ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y) if ntoc < len(toc_idx) - 1: if index == toc_idx[ntoc + 1]: try: y = toc_secid[toc_sect[ntoc + 1]] except KeyError: pass else: return toc_idx[ ntoc + 1] - index, width, 0, None, toc_sect[ntoc + 1] elif k in K["PrevChapter"]: ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y) if ntoc > 0: if index == toc_idx[ntoc - 1]: y = toc_secid.get(toc_sect[ntoc - 1], 0) else: return toc_idx[ ntoc - 1] - index, width, 0, None, toc_sect[ntoc - 1] elif k in K["BeginningOfCh"]: ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y) try: y = toc_secid[toc_sect[ntoc]] except KeyError: y = 0 elif k in K["EndOfCh"]: ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y) try: if toc_secid[toc_sect[ntoc + 1]] - rows >= 0: y = toc_secid[toc_sect[ntoc + 1]] - rows else: y = toc_secid[toc_sect[ntoc]] except (KeyError, IndexError): y = pgend(totlines, rows) elif k in K["ToC"]: ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y) fllwd = toc(toc_name, ntoc) if fllwd is not None: if fllwd in {curses.KEY_RESIZE } | K["Help"] | K["Metadata"]: k = fllwd continue if index == toc_idx[fllwd]: try: y = toc_secid[toc_sect[fllwd]] except KeyError: y = 0 else: return toc_idx[ fllwd] - index, width, 0, None, toc_sect[fllwd] elif k in K["Metadata"]: k = meta(ebook) if k in {curses.KEY_RESIZE} | K["Help"] | K["ToC"]: continue elif k in K["Help"]: k = help() if k in {curses.KEY_RESIZE} | K["Metadata"] | K["ToC"]: continue elif k in K["Enlarge"] and (width + count) < cols - 2: width += count return 0, width, 0, y / totlines, "" elif k in K["Shrink"] and width >= 22: width -= count return 0, width, 0, y / totlines, "" elif k in K["SetWidth"]: if countstring == "": # if called without a count, toggle between 80 cols and full width if width != 80 and cols - 2 >= 80: return 0, 80, 0, y / totlines, "" else: return 0, cols - 2, 0, y / totlines, "" else: width = count if width < 20: width = 20 elif width >= cols - 2: width = cols - 2 return 0, width, 0, y / totlines, "" # elif k == ord("0"): # if width != 80 and cols - 2 >= 80: # return 0, 80, 0, y/totlines, "" # else: # return 0, cols - 2, 0, y/totlines, "" elif k in K["RegexSearch"]: fs = searching(pad, src_lines, width, y, index, len(contents)) if fs == curses.KEY_RESIZE: k = fs continue elif SEARCHPATTERN is not None: return fs, width, 0, None, "" else: y = fs elif k in K["OpenImage"] and VWR is not None: gambar, idx = [], [] for n, i in enumerate(src_lines[y:y + rows]): img = re.search("(?<=\\[IMG:)[0-9]+(?=\\])", i) if img is not None: gambar.append(img.group()) idx.append(n) impath = "" if len(gambar) == 1: impath = imgs[int(gambar[0])] elif len(gambar) > 1: p, i = 0, 0 while p not in K["Quit"] and p not in K["Follow"]: SCREEN.move(idx[i], x + width // 2 + len(gambar[i]) + 1) SCREEN.refresh() curses.curs_set(1) p = pad.getch() if p in K["ScrollDown"]: i += 1 elif p in K["ScrollUp"]: i -= 1 i = i % len(gambar) curses.curs_set(0) if p in K["Follow"]: impath = imgs[int(gambar[i])] if impath != "": imgsrc = dots_path(chpath, impath) k = open_media(pad, ebook, imgsrc) continue elif k in K["SwitchColor"] and COLORSUPPORT and countstring in { "", "0", "1", "2" }: if countstring == "": count_color = curses.pair_number(SCREEN.getbkgd()) if count_color not in {2, 3}: count_color = 1 count_color = count_color % 3 else: count_color = count SCREEN.bkgd(curses.color_pair(count_color + 1)) return 0, width, y, None, "" elif k in K["ShowHideProgress"] and CFG[ "EnableProgressIndicator"]: SHOWPROGRESS = not SHOWPROGRESS elif k == curses.KEY_RESIZE: savestate(ebook.path, index, width, y, y / totlines) # stated in pypi windows-curses page: # to call resize_term right after KEY_RESIZE if sys.platform == "win32": curses.resize_term(rows, cols) rows, cols = SCREEN.getmaxyx() else: rows, cols = SCREEN.getmaxyx() curses.resize_term(rows, cols) if cols < 22 or rows < 12: sys.exit("ERR: Screen was too small.") if cols <= width: return 0, cols - 2, 0, y / totlines, "" else: return 0, width, y, None, "" countstring = "" if CFG["EnableProgressIndicator"]: PROGRESS = (TOTALLOCALPCTG + sum(LOCALPCTG[:y + rows - 1])) / TOTALPCTG PROGRESSTR = "{}%".format(int(PROGRESS * 100)) try: SCREEN.clear() SCREEN.addstr(0, 0, countstring) if SHOWPROGRESS and (cols - width - 2) // 2 > 3: SCREEN.addstr(0, cols - len(PROGRESSTR), PROGRESSTR) SCREEN.refresh() if totlines - y < rows: pad.refresh(y, 0, 0, x, totlines - y, x + width) else: pad.refresh(y, 0, 0, x, rows - 1, x + width) except curses.error: pass k = pad.getch() except KeyboardInterrupt: savestate(ebook.path, index, width, y, y / totlines) sys.exit()
def loop(self, debug: bool = False) -> None: """The key-handling event loop. This method takes care of reading the user's key strokes and triggering associated commands. Args: debug: if True, the key-event loop is not automatically started. """ key = 0 # key is the last character pressed while True: # handle possible keys try: if key in TUI.KEYDICT: cmd = TUI.KEYDICT[key] if cmd not in STATE.inactive_commands: if isinstance(cmd, tuple): TUI.COMMANDS[cmd[0]](self, cmd[1]) else: TUI.COMMANDS[cmd](self) elif key == curses.KEY_RESIZE: self.resize_handler(None, None) except StopIteration: LOGGER.debug("Stopping key event loop.") # raised by quit command sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ break # highlight current line current_attributes: List[Optional[int]] = [] for x_pos in range(0, self.viewport.pad.getmaxyx()[1]): x_ch = self.viewport.pad.inch(STATE.current_line, x_pos) # store attributes by filtering out the character text current_attributes.append(x_ch & curses.A_CHARTEXT) # extract color information x_color = x_ch & curses.A_COLOR color_pair_content = curses.pair_content( curses.pair_number(x_color)) # if the current color attribute is lower in priority than the current line # highlighting, use that instead if all(col <= 0 for col in color_pair_content): self.viewport.pad.chgat( STATE.current_line, x_pos, 1, curses.color_pair( TUI.COLOR_NAMES.index("cursor_line") + 1), ) else: # otherwise, we remove the stored attributes in order to not reset them later current_attributes[-1] = None for stream, name in zip((self.stderr, self.stdout), ("stderr", "stdout")): stream.flush() if stream.lines: stream.split() LOGGER.info("sys.%s contains:\n%s", name, "\n".join(stream.lines)) # wrap before checking the height: stream.wrap(self.width) if stream.height > 1 and not debug: stream.popup( self, background=TUI.COLOR_NAMES.index(f"popup_{name}")) else: self.prompt_print(stream.lines) stream.clear() # Refresh the screen self.viewport.refresh() # Wait for next input key = self.stdscr.getch() LOGGER.debug("Key press registered: %s", str(key)) # reset highlight of current line for x_pos in range(0, self.viewport.pad.getmaxyx()[1]): attr = current_attributes[x_pos] if attr is not None: self.viewport.pad.chgat(STATE.current_line, x_pos, 1, attr)
#