import os, re, string from point import Point # NOTE: i should write a python module to build a statemachine from a terminfo # entry. otherwise this project is probably hopeless. def show(c): if c in string.ascii_letters + string.digits + string.punctuation + ' ': return c else: return '\\%03o' % ord(c) cbuffer_map = { 'black': 'B', 'blue': 'b', 'cyan': 'c', 'default': 'd', 'green': 'g', 'magenta': 'm', 'red': 'r', 'white': 'w', 'yellow': 'y', } def make_cbuf(fg, bg, xt): if 'bold' in xt and fg == 'default': fg = 'cyan' if 'italic' in xt and fg == 'default': fg = 'yellow' if 'underline' in xt and fg == 'default': fg = 'green' if 'inverse' in xt and fg == 'default': fg = 'magenta' if 'strike' in xt and fg == 'default': fg = 'red' if 'bold' in xt: return '[%s:%s:*]' % (cbuffer_map[fg], cbuffer_map[bg]) else: return '[%s:%s]' % (cbuffer_map[fg], cbuffer_map[bg]) class Dumb: name = 'dumb' cbuf = False def _term_insert(self, s): assert self.i <= len(self.outc) if self.i == len(self.outc): self.outc.append(s) else: self.outc[self.i] = s self.i += 1 def term_do_clear(self): self.outs = '' self.i = 0 self.outc = [] if self.cbuf: self._term_insert(make_cbuf(self._fg, self._bg, self._xt)) def term_do_clear_bol(self): pass def term_do_clear_eol(self): del self.outc[self.i:] if self.cbuf: self._term_insert(make_cbuf(self._fg, self._bg, self._xt)) def term_do_clear_eos(self): pass def term_do_home(self): self.outs = '' self.i = 0 self.outc = [] if self.cbuf: self._term_insert(make_cbuf(self._fg, self._bg, self._xt)) def term_do_backspace(self): self.i = max(0, self.i - 1) def term_do_tab(self): self._term_insert(' ') def term_do_newline(self): self.outs += ''.join(self.outc) + '\n' self.i = 0 self.outc = [] if self.cbuf: self._term_insert(make_cbuf(self._fg, self._bg, self._xt)) def term_do_creturn(self): self.i = 0 def term_do_esc(self, c): pass def term_do_delete(self): if self.i < len(self.outc): del self.outc[self.i] def term_handle_ctl(self, c): n = ord(c) if n == 8: self.term_do_backspace() elif n == 9: self.term_do_tab() elif n == 10: self.term_do_newline() elif n == 12: self.term_do_clear() elif n == 13: self.term_do_creturn() elif n == 27: self.term_do_esc(c) elif n == 127: self.term_do_delete() def term_handle_print(self, c): if self.cbuf: if c in ('\\', '[', ']'): self._term_insert('\\') self._term_insert(c) def term_handle_8bit(self, c): pass def term_handle(self, c): n = ord(c) assert n >= 0 and n < 256 if n <= 27 or n == 127: self.term_handle_ctl(c) elif n < 127: self.term_handle_print(c) else: self.term_handle_8bit(c) def term_receive(self, s): for c in s: self.term_handle(c) def term_filter(self, s): self.i = 0 self.outc = [] self.outs = "" self.term_receive(s) return self.outs + ''.join(self.outc) class XTerm(Dumb): name = 'xterm' termtype = 'xterm' comment_re = re.compile('^ *#') header_re = re.compile('^[a-zA-Z0-9]+\|') bool_re = re.compile('^([a-zA-Z0-9]+)$') num_re = re.compile('^([a-zA-Z0-9]+)#(.+)$') str_re = re.compile('^([a-zA-Z0-9]+)=(.+)$') style_re = re.compile(r'^\033\[[0-9;]*m') text_signal_re = re.compile(r'^\033\][0-9]+;.+\007') cup_re = re.compile(r'^\033\[[0-9]+;[0-9]+H') callbacks = { 'clear': 'term_do_clear', 'home': 'term_do_home', 'rmm': 'term_nop', 'smm': 'term_nop', 'bold': 'term_nop', 'op': 'term_nop', #original color-pair 'cup': 'term_nop', #hmmm? 'cnorm': 'term_nop', #make cursor normal 'civis': 'term_nop', #make cursor inviz 'cvvis': 'term_nop', #make cursor bold 'smcup': 'term_nop', #enter cup mode 'rmcup': 'term_nop', #leave cup mode 'smacs': 'term_nop', #orig charset 'rmacs': 'term_nop', # 'smso': 'term_nop', #enter standout 'rmso': 'term_nop', #leave standout 'smul': 'term_nop', #enter underline 'rmul': 'term_nop', #leave underline 'smkx': 'term_nop', #enter keypad junk 'rmkx': 'term_nop', #leave keypad junk 'ed': 'term_do_clear_eos', 'el': 'term_do_clear_eol', #'el1': 'term_nop', } def __init__(self, cbuf=False): self.debug = False self.cbuf = cbuf # style info self._fg = 'default' self._bg = 'default' self._xt = set() self._meta = [] f = os.popen('infocmp %s' % self.termtype, 'r') self.sequences = {} for line in f: if self.comment_re.match(line) or self.header_re.match(line): continue for field in [x.strip() for x in line.split(',')]: if not field: continue elif self.bool_re.match(field) or self.num_re.match(field): continue m = self.str_re.match(field) assert m, "huh?? %r" % field name, val = m.groups() if len(val) > 3 and val[:3] == '\\E[' and val[3] in '0123456789': continue elif val.startswith('\\E'): #if val == '\\E[1m': # raise Exception("NONONONO") self.sequences[val.replace('\\E', '\033')] = name f.close() def parse_style(self, s): # starts with '\033[' and ends with 'm' s2 = s[2:-1] #if s not in ('\x1b[0m', '\x1b[22m'): # raise Exception('argh: %r' % s) if s2 == '': l = ['0'] else: l = s2.split(';') for n in l: if n == '0': self._fg = 'default' self._bg = 'default' self._xt = set() # xtra attributes elif n == '1': self._xt.add('bold') elif n == '3': self._xt.add('italic') elif n == '4': self._xt.add('underline') elif n == '7': self._xt.add('inverse') elif n == '9': self._xt.add('strike') elif n == '22': self._xt.discard('bold') elif n == '23': self._xt.discard('italic') elif n == '24': self._xt.discard('underline') elif n == '27': self._xt.discard('inverse') elif n == '29': self._xt.discard('strike') # foreground elif n == '30': self._fg = 'black' elif n == '31': self._fg = 'red' elif n == '32': self._fg = 'green' elif n == '33': self._fg = 'yellow' elif n == '34': self._fg = 'blue' elif n == '35': self._fg = 'magenta' elif n == '36': self._fg = 'cyan' elif n == '37': self._fg = 'white' elif n == '39': self._fg = 'default' # background elif n == '40': self._bg = 'black' elif n == '41': self._bg = 'red' elif n == '42': self._bg = 'green' elif n == '43': self._bg = 'yellow' elif n == '44': self._bg = 'blue' elif n == '45': self._bg = 'magenta' elif n == '46': self._bg = 'cyan' elif n == '47': self._bg = 'white' elif n == '49': self._bg = 'default' def term_do_style(self): if self.cbuf: self._term_insert(make_cbuf(self._fg, self._bg, self._xt)) def term_nop(self, *args): pass def term_filter(self, s): self._meta = [] return Dumb.term_filter(self, s) def term_do_esc(self, c): self._meta.append(c) def term_handle(self, c): #UGHGHGHGH if self._meta: self._meta.append(c) s = ''.join(self._meta) if s.startswith('\x1b[H') and not '\x1b[H\x1b[2J'.startswith(s): self._meta = self._meta[3:] if self._meta[0] != '\x1b': for c2 in self._meta[:-1]: Dumb.term_handle(self, c2) self._meta = [] if self._meta: s = ''.join(self._meta) if s in self.sequences: name = self.sequences[s] if self.debug: self._term_insert('<%s>' % name) elif name in self.callbacks: f = getattr(self, self.callbacks[name]) f() else: self._term_insert('<%s>' % name) # certain sequences shouldn't get removed immediately; ugh if name not in ['home']: self._meta = [] elif self.style_re.match(s): self.parse_style(s) self.term_do_style() self._meta = [] elif self.text_signal_re.match(s): self._meta = [] elif self.cup_re.match(s): self._meta = [] elif s.endswith('\n'): self._term_insert(''.join([show(c) for c in self._meta])) self._meta = [] else: Dumb.term_handle(self, c) # terminfo junk boolean_settings = [ ['cpi_changes_res', 'cpix'], ['cr_cancels_micro_mode', 'crxm'], ['dest_tabs_magic_smso', 'xt'], ['eat_newline_glitch', 'xenl'], ['erase_overstrike', 'eo'], ['generic_type', 'gn'], ['hard_copy', 'hc'], ['hard_cursor', 'chts'], ['has_meta_key', 'km'], ['has_print_wheel', 'daisy'], ['has_status_line', 'hs'], ['hue_lightness_saturation', 'hls'], ['insert_null_glitch', 'in'], ['lpi_changes_res', 'lpix'], ['memory_above', 'da'], ['memory_below', 'db'], ['move_insert_mode', 'mir'], ['move_standout_mode', 'msgr'], ['needs_xon_xoff', 'nxon'], ['no_esc_ctlc', 'xsb'], ['no_pad_char', 'npc'], ['non_dest_scroll_region', 'ndscr'], ['non_rev_rmcup', 'nrrmc'], ['over_strike', 'os'], ['prtr_silent', 'mc5i'], ['row_addr_glitch', 'xvpa'], ['semi_auto_right_margin', 'sam'], ['status_line_esc_ok', 'eslok'], ['tilde_glitch', 'hz'], ['transparent_underline', 'ul'], ['xon_xoff', 'xon'], ] number_settings = [ ['columns', 'cols'], ['init_tabs', 'it'], ['label_height', 'lh'], ['label_width', 'lw'], ['lines', 'lines'], ['lines_of_memory', 'lm'], ['magic_cookie_glitch', 'xmc'], ['max_attributes', 'ma'], ['max_colors', 'colors'], ['max_pairs', 'pairs'], ['maximum_windows', 'wnum'], ['no_color_video', 'ncv'], ['num_labels', 'nlab'], ['padding_baud_rate', 'pb'], ['virtual_terminal', 'vt'], ['width_status_line', 'wsl'], ] string_settings = [ ['acs_chars', 'acsc'], ['back_tab', 'cbt'], ['bell', 'bel'], ['carriage_return', 'cr'], ['change_char_pitch', 'cpi'], ['change_line_pitch', 'lpi'], ['change_res_horz', 'chr'], ['change_res_vert', 'cvr'], ['change_scroll_region', 'csr'], ['char_padding', 'rmp'], ['clear_all_tabs', 'tbc'], ['clear_margins', 'mgc'], ['clear_screen', 'clear'], ['clr_bol', 'el1'], ['clr_eol', 'el'], ['clr_eos', 'ed'], ['column_address', 'hpa'], ['command_character', 'cmdch'], ['create_window', 'cwin'], ['cursor_address', 'cup'], ['cursor_down', 'cud1'], ['cursor_home', 'home'], ['cursor_invisible', 'civis'], ['cursor_left', 'cub1'], ['cursor_mem_address', 'mrcup'], ['cursor_normal', 'cnorm'], ['cursor_right', 'cuf1'], ['cursor_to_ll', 'll'], ['cursor_up', 'cuu1'], ['cursor_visible', 'cvvis'], ['define_char', 'defc'], ['delete_character', 'dch1'], ['delete_line', 'dl1'], ['dial_phone', 'dial'], ['dis_status_line', 'dsl'], ['display_clock', 'dclk'], ['down_half_line', 'hd'], ['ena_acs', 'enacs'], ['enter_alt_charset_mode', 'smacs'], ['enter_am_mode', 'smam'], ['enter_blink_mode', 'blink'], ['enter_bold_mode', 'bold'], ['enter_ca_mode', 'smcup'], ['enter_delete_mode', 'smdc'], ['enter_dim_mode', 'dim'], ['enter_doublewide_mode', 'swidm'], ['enter_draft_quality', 'sdrfq'], ['enter_insert_mode', 'smir'], ['enter_italics_mode', 'sitm'], ['enter_leftward_mode', 'slm'], ['enter_micro_mode', 'smicm'], ['enter_near_letter_quality', 'snlq'], ['enter_normal_quality', 'snrmq'], ['enter_protected_mode', 'prot'], ['enter_reverse_mode', 'rev'], ['enter_secure_mode', 'invis'], ['enter_shadow_mode', 'sshm'], ['enter_standout_mode', 'smso'], ['enter_subscript_mode', 'ssubm'], ['enter_superscript_mode', 'ssupm'], ['enter_underline_mode', 'smul'], ['enter_upward_mode', 'sum'], ['enter_xon_mode', 'smxon'], ['erase_chars', 'ech'], ['exit_alt_charset_mode', 'rmacs'], ['exit_am_mode', 'rmam'], ['exit_attribute_mode', 'sgr0'], ['exit_ca_mode', 'rmcup'], ['exit_delete_mode', 'rmdc'], ['exit_doublewide_mode', 'rwidm'], ['exit_insert_mode', 'rmir'], ['exit_italics_mode', 'ritm'], ['exit_leftward_mode', 'rlm'], ['exit_micro_mode', 'rmicm'], ['exit_shadow_mode', 'rshm'], ['exit_standout_mode', 'rmso'], ['exit_subscript_mode', 'rsubm'], ['exit_superscript_mode', 'rsupm'], ['exit_underline_mode', 'rmul'], ['exit_upward_mode', 'rum'], ['exit_xon_mode', 'rmxon'], ['fixed_pause', 'pause'], ['flash_hook', 'hook'], ['flash_screen', 'flash'], ['form_feed', 'ff'], ['from_status_line', 'fsl'], ['goto_window', 'wingo'], ['hangup', 'hup'], ['init_1string', 'is1'], ['init_2string', 'is2'], ['init_3string', 'is3'], ['init_file', 'if'], ['init_prog', 'iprog'], ['initialize_color', 'initc'], ['initialize_pair', 'initp'], ['insert_character', 'ich1'], ['insert_line', 'il1'], ['insert_padding', 'ip'], ['key_a1', 'ka1'], ['key_a3', 'ka3'], ['key_b2', 'kb2'], ['key_backspace', 'kbs'], ['key_beg', 'kbeg'], ['key_btab', 'kcbt'], ['key_c1', 'kc1'], ['key_c3', 'kc3'], ['key_cancel', 'kcan'], ['key_catab', 'ktbc'], ['key_clear', 'kclr'], ['key_close', 'kclo'], ['key_command', 'kcmd'], ['key_copy', 'kcpy'], ['key_create', 'kcrt'], ['key_ctab', 'kctab'], ['key_dc', 'kdch1'], ['key_dl', 'kdl1'], ['key_down', 'kcud1'], ['key_eic', 'krmir'], ['key_end', 'kend'], ['key_enter', 'kent'], ['key_eol', 'kel'], ['key_eos', 'ked'], ['key_exit', 'kext'], ['key_f0', 'kf0'], ['key_f1', 'kf1'], ['key_f10', 'kf10'], ['key_f11', 'kf11'], ['key_f12', 'kf12'], ['key_f13', 'kf13'], ['key_f14', 'kf14'], ['key_f15', 'kf15'], ['key_f16', 'kf16'], ['key_f17', 'kf17'], ['key_f18', 'kf18'], ['key_f19', 'kf19'], ['key_f2', 'kf2'], ['key_f20', 'kf20'], ['key_f21', 'kf21'], ['key_f22', 'kf22'], ['key_f23', 'kf23'], ['key_f24', 'kf24'], ['key_f25', 'kf25'], ['key_f26', 'kf26'], ['key_f27', 'kf27'], ['key_f28', 'kf28'], ['key_f29', 'kf29'], ['key_f3', 'kf3'], ['key_f30', 'kf30'], ['key_f31', 'kf31'], ['key_f32', 'kf32'], ['key_f33', 'kf33'], ['key_f34', 'kf34'], ['key_f35', 'kf35'], ['key_f36', 'kf36'], ['key_f37', 'kf37'], ['key_f38', 'kf38'], ['key_f39', 'kf39'], ['key_f4', 'kf4'], ['key_f40', 'kf40'], ['key_f41', 'kf41'], ['key_f42', 'kf42'], ['key_f43', 'kf43'], ['key_f44', 'kf44'], ['key_f45', 'kf45'], ['key_f46', 'kf46'], ['key_f47', 'kf47'], ['key_f48', 'kf48'], ['key_f49', 'kf49'], ['key_f5', 'kf5'], ['key_f50', 'kf50'], ['key_f51', 'kf51'], ['key_f52', 'kf52'], ['key_f53', 'kf53'], ['key_f54', 'kf54'], ['key_f55', 'kf55'], ['key_f56', 'kf56'], ['key_f57', 'kf57'], ['key_f58', 'kf58'], ['key_f59', 'kf59'], ['key_f6', 'kf6'], ['key_f60', 'kf60'], ['key_f61', 'kf61'], ['key_f62', 'kf62'], ['key_f63', 'kf63'], ['key_f7', 'kf7'], ['key_f8', 'kf8'], ['key_f9', 'kf9'], ['key_find', 'kfnd'], ['key_help', 'khlp'], ['key_home', 'khome'], ['key_ic', 'kich1'], ['key_il', 'kil1'], ['key_left', 'kcub1'], ['key_ll', 'kll'], ['key_mark', 'kmrk'], ['key_message', 'kmsg'], ['key_move', 'kmov'], ['key_next', 'knxt'], ['key_npage', 'knp'], ['key_open', 'kopn'], ['key_options', 'kopt'], ['key_ppage', 'kpp'], ['key_previous', 'kprv'], ['key_print', 'kprt'], ['key_redo', 'krdo'], ['key_reference', 'kref'], ['key_refresh', 'krfr'], ['key_replace', 'krpl'], ['key_restart', 'krst'], ['key_resume', 'kres'], ['key_right', 'kcuf1'], ['key_save', 'ksav'], ['key_sbeg', 'kBEG'], ['key_scancel', 'kCAN'], ['key_scommand', 'kCMD'], ['key_scopy', 'kCPY'], ['key_screate', 'kCRT'], ['key_sdc', 'kDC'], ['key_sdl', 'kDL'], ['key_select', 'kslt'], ['key_send', 'kEND'], ['key_seol', 'kEOL'], ['key_sexit', 'kEXT'], ['key_sf', 'kind'], ['key_sfind', 'kFND'], ['key_shelp', 'kHLP'], ['key_shome', 'kHOM'], ['key_sic', 'kIC'], ['key_sleft', 'kLFT'], ['key_smessage', 'kMSG'], ['key_smove', 'kMOV'], ['key_snext', 'kNXT'], ['key_soptions', 'kOPT'], ['key_sprevious', 'kPRV'], ['key_sprint', 'kPRT'], ['key_sr', 'kri'], ['key_sredo', 'kRDO'], ['key_sreplace', 'kRPL'], ['key_sright', 'kRIT'], ['key_srsume', 'kRES'], ['key_ssave', 'kSAV'], ['key_ssuspend', 'kSPD'], ['key_stab', 'khts'], ['key_sundo', 'kUND'], ['key_suspend', 'kspd'], ['key_undo', 'kund'], ['key_up', 'kcuu1'], ['keypad_local', 'rmkx'], ['keypad_xmit', 'smkx'], ['lab_f0', 'lf0'], ['lab_f1', 'lf1'], ['lab_f10', 'lf10'], ['lab_f2', 'lf2'], ['lab_f3', 'lf3'], ['lab_f4', 'lf4'], ['lab_f5', 'lf5'], ['lab_f6', 'lf6'], ['lab_f7', 'lf7'], ['lab_f8', 'lf8'], ['lab_f9', 'lf9'], ['label_format', 'fln'], ['label_off', 'rmln'], ['label_on', 'smln'], ['meta_off', 'rmm'], ['meta_on', 'smm'], ['micro_column_address', 'mhpa'], ['micro_down', 'mcud1'], ['micro_left', 'mcub1'], ['micro_right', 'mcuf1'], ['micro_row_address', 'mvpa'], ['micro_up', 'mcuu1'], ['newline', 'nel'], ['order_of_pins', 'porder'], ['orig_colors', 'oc'], ['orig_pair', 'op'], ['pad_char', 'pad'], ['parm_dch', 'dch'], ['parm_delete_line', 'dl'], ['parm_down_cursor', 'cud'], ['parm_down_micro', 'mcud'], ['parm_ich', 'ich'], ['parm_index', 'indn'], ['parm_insert_line', 'il'], ['parm_left_cursor', 'cub'], ['parm_left_micro', 'mcub'], ['parm_right_cursor', 'cuf'], ['parm_right_micro', 'mcuf'], ['parm_rindex', 'rin'], ['parm_up_cursor', 'cuu'], ['parm_up_micro', 'mcuu'], ['pkey_key', 'pfkey'], ['pkey_local', 'pfloc'], ['pkey_xmit', 'pfx'], ['plab_norm', 'pln'], ['print_screen', 'mc0'], ['prtr_non', 'mc5p'], ['prtr_off', 'mc4'], ['prtr_on', 'mc5'], ['pulse', 'pulse'], ['quick_dial', 'qdial'], ['remove_clock', 'rmclk'], ['repeat_char', 'rep'], ['req_for_input', 'rfi'], ['reset_1string', 'rs1'], ['reset_2string', 'rs2'], ['reset_3string', 'rs3'], ['reset_file', 'rf'], ['restore_cursor', 'rc'], ['row_address', 'vpa'], ['save_cursor', 'sc'], ['scroll_forward', 'ind'], ['scroll_reverse', 'ri'], ['select_char_set', 'scs'], ['set_attributes', 'sgr'], ['set_background', 'setb'], ['set_bottom_margin', 'smgb'], ['set_bottom_margin_parm', 'smgbp'], ['set_clock', 'sclk'], ['set_color_pair', 'scp'], ['set_foreground', 'setf'], ['set_left_margin', 'smgl'], ['set_left_margin_parm', 'smglp'], ['set_right_margin', 'smgr'], ['set_right_margin_parm', 'smgrp'], ['set_tab', 'hts'], ['set_top_margin', 'smgt'], ['set_top_margin_parm', 'smgtp'], ['set_window', 'wind'], ['start_bit_image', 'sbim'], ['start_char_set_def', 'scsd'], ['stop_bit_image', 'rbim'], ['stop_char_set_def', 'rcsd'], ['subscript_characters', 'subcs'], ['superscript_characters', 'supcs'], ['tab', 'ht'], ['these_cause_cr', 'docr'], ['to_status_line', 'tsl'], ['tone', 'tone'], ['underline_char', 'uc'], ['up_half_line', 'hu'], ['user0', 'u0'], ['user1', 'u1'], ['user2', 'u2'], ['user3', 'u3'], ['user4', 'u4'], ['user5', 'u5'], ['user6', 'u6'], ['user7', 'u7'], ['user8', 'u8'], ['user9', 'u9'], ['wait_tone', 'wait'], ['xoff_character', 'xoffc'], ['xon_character', 'xonc'], ['zero_motion', 'zerom'], ]