Beispiel #1
0
    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
Beispiel #2
0
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
Beispiel #3
0
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)
Beispiel #4
0
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))
Beispiel #5
0
 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)
Beispiel #6
0
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
Beispiel #7
0
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)
Beispiel #8
0
 def current_fg(self):
     colorpair = curses.pair_number(colors.get(self.current_color))
     fg, bg = curses.pair_content(colorpair)
     return f'{fg}'
Beispiel #9
0
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)
Beispiel #10
0
 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()
Beispiel #11
0
    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)
Beispiel #12
0
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"
Beispiel #13
0
#
Beispiel #14
0
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()
Beispiel #15
0
    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)
Beispiel #16
0
#