📄 macros.c
字号:
/*--------------------------------*-C-*----------------------------------------* * File: macros.c (used to be hotkeys.c) *-----------------------------------------------------------------------------* * * All portions of code are copyright by their respective author/s. * * Copyright (c) 2005-2006 Gautam Iyer <gi1242@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 675 Mass * Ave, Cambridge, MA 02139, USA. *----------------------------------------------------------------------------*/#include "../config.h"#include "rxvt.h"#ifdef DEBUG_VERBOSE#define DEBUG_LEVEL 1#else #define DEBUG_LEVEL 0#endif#if DEBUG_LEVEL#define DBG_MSG(d,x) if(d <= DEBUG_LEVEL) fprintf x#else#define DBG_MSG(d,x)#endif/* * Must sync these to values for macro_t.type in rxvtlib.h. */static const char *const macroNames[] ={ "Dummy", /* Delete macro */ "Esc", /* Escape sequence to send to mrxvt */ "Str", /* String to send to child process */ "NewTab", /* Open a new tab, and exec a program in it. */ "Close", /* Close tab(s) */ "GotoTab", /* Switch to tab */ "MoveTab", /* Move tab */ "Scroll", /* Scroll up/down */ "Copy", /* Copy selection */ "Paste", /* Paste selection */ "ToggleSubwin", /* Toggle subwindows (scroll / menu / tabbar) */ "ResizeFont", /* Resize terminal font */ "ToggleVeryBold", /* Toggle use of bold font for colored text */ "ToggleTransparency", /* Toggle pseudo transparency */ "ToggleBroadcast", /* Toggle broadcasting of input */ "ToggleHold", /* Toggle holding of completed tabs */ "ToggleFullscreen", /* Toggle full screen mode */ "SetTitle", /* Set title of active tab to selection */ "PrintScreen", /* Dump screen to file / printer */ "SaveConfig", /* Save config to file */ "ToggleMacros" /* Toggle using keyboard macros */};/******************************************************************************\* BEGIN INTERNAL ROUTINE PROTOTYPES *\******************************************************************************/int macro_cmp ( const void*, const void*);int rxvt_add_macro ( rxvt_t*, KeySym, unsigned char, char*, Bool, Bool);unsigned char macro_set_number ( unsigned char, unsigned char);/******************************************************************************\* END INTERNAL ROUTINE PROTOTYPES *\******************************************************************************//* * 2006-02-23 gi1242: New macro code. This should extend the hotkey aproach * without causing code bloat. The idea is that defining "macros" can also * enable the user to communicate with mrxvt using escape sequences! *//* {{{ Macro action functions (called from rxvt_dispatch_action) *//* EXTPROTO */voidrxvt_toggle_verybold( rxvt_t *r ){ if (r->Options2 & Opt2_veryBold) r->Options2 &= ~Opt2_veryBold; else r->Options2 |= Opt2_veryBold; rxvt_scr_touch (r, ATAB(r), True);}/* * str = [+-][s|t|m|b] * * '+' means show. '-' means hide. Neither means toggle. * * s, t, m, b are the scroll tab and menubars respectively. 'b' is tabbar * buttons. * * 2006-02-23 gi1242 TODO: Permit hiding / showing more than one sub window at * one time. Currently calls to resize_on_subwin must be followed by * ConfigureNotify event (which we process) to correct the sizeHint (otherwise * we end up having the wrong size). *//* EXTPROTO */voidrxvt_toggle_subwin( rxvt_t *r, const unsigned char *str){ if( str == NULL || *str == '\0' || STRCHR( str, 't') || STRCHR( str, 'T' ) ) { /* * Backward compatibility -- If str is NULL or empty, then toggle the * tabbar visibility. */ if( rxvt_tabbar_visible (r) && ( str == NULL || *str != '+' )) { /* * If the tabbar is visible, and we are not forced to show it then * hide it. */ if( rxvt_tabbar_hide(r) ) rxvt_resize_on_subwin (r, HIDE_TABBAR); } else if( !rxvt_tabbar_visible( r ) && ( str == NULL || *str != '-' )) { /* * If the tabbar is hidden, and we are not forced to hide it, then * show the tabbar. */ if( rxvt_tabbar_show(r)) rxvt_resize_on_subwin (r, SHOW_TABBAR); } return; } /* * The remainder of this function assumes a non-empty string. */ if( STRCHR( str, 'b') || STRCHR( str, 'B') ) { /* Toggle tabbar buttons */ switch( *str ) { case '+': /* Show buttons */ r->Options2 &= ~Opt2_hideButtons; break; case '-': /* Hide buttons */ r->Options2 |= Opt2_hideButtons; break; default: /* Toggle buttons */ r->Options2 ^= Opt2_hideButtons; } /* Refresh tabbar */ rxvt_tabbar_set_visible_tabs (r, False); if( r->tabBar.win != None) XClearArea( r->Xdisplay, r->tabBar.win, 0, 0, 0, 0, True); return; }#ifdef HAVE_SCROLLBARS if( STRCHR( str, 's') || STRCHR( str, 'S' ) ) { if( rxvt_scrollbar_visible (r) && *str != '+' ) { if( rxvt_scrollbar_hide(r) ) rxvt_resize_on_subwin (r, HIDE_SCROLLBAR); } else if( !rxvt_scrollbar_visible( r ) && *str != '-' ) { if( rxvt_scrollbar_show(r) ) rxvt_resize_on_subwin (r, SHOW_SCROLLBAR); } return; }#endif#ifdef HAVE_MENUBAR if( STRCHR( str, 'm') || STRCHR( str, 'M' ) ) { if( rxvt_menubar_visible (r) && *str != '+' ) { if( rxvt_menubar_hide(r) ) rxvt_resize_on_subwin (r, HIDE_MENUBAR); } else if( !rxvt_menubar_visible( r ) && *str != '-' ) { if( rxvt_menubar_show(r) ) rxvt_resize_on_subwin (r, SHOW_MENUBAR); } return; }#endif}/* }}} *//* * Functions to parse macros (add them to our list), and exec actions. *//* {{{1 macro_cmp() * * Used by bsearch and qsort for macro comparison. */intmacro_cmp( const void *p1, const void *p2){ const macros_t *macro1 = p1, *macro2 = p2; /* First compare keysyms, then modflags. Ignore the "Primary" modifier */ return (macro1->keysym == macro2->keysym) ? (macro1->modFlags & ~MACRO_PRIMARY) - (macro2->modFlags & ~MACRO_PRIMARY) : macro1->keysym - macro2->keysym;}/* {{{1 macro_set_number( flag, num ) *//* INTPROTO */unsigned charmacro_set_number( unsigned char flag, unsigned char num ){ flag &= MACRO_MODMASK; flag |= (num << MACRO_N_MOD_BITS); return flag;}/* {{{1 rxvt_parse_macros(str, arg): Parse macro from arguments * * str and arg can be as follows: * * 1. str = keyname, arg = argument. * 2. str = macro.keyname: argument, arg = NULL * * If a valid macro is found, it is added to our list (r->macros) of macros. * Returns 1 if the macro is added to r->macros, 0 if it is not a macro (i.e. * does not begin with "macro."), and -1 if invalid syntax / error adding / etc. *//* EXTPROTO */#define NEWARGLIM 500 /* `reasonable' size */intrxvt_parse_macros( rxvt_t *r, const char *str, const char *arg, Bool noReplace){ char newarg[NEWARGLIM], keyname[ NEWARGLIM], *keyname_nomods; unsigned char modFlags = 0; KeySym keysym; Bool addmacro = False; if( arg == NULL ) { char *keyend; int n; /* * Need to split str into keyname and argument. */ if( (n = rxvt_str_match(str, "macro.")) == 0 ) return 0; str += n; /* skip `macro.' */ if( (keyend = STRCHR( str, ':' )) == NULL ) return -1; n = min( keyend - str, NEWARGLIM - 1 ); STRNCPY( keyname, str, n ); keyname[n] = 0; STRNCPY( newarg, keyend + 1, NEWARGLIM - 1 ); } else { /* * Keyname is already in str. Copy arg into newarg. */ STRNCPY( keyname, str, NEWARGLIM - 1 ); keyname[ NEWARGLIM - 1] = '\0'; STRNCPY( newarg, arg, NEWARGLIM - 1 ); } /* Null terminate and strip leading / trailing spaces */ newarg[NEWARGLIM - 1] = '\0'; rxvt_str_trim( newarg ); DBG_MSG( 1, ( stderr, "Got macro '%s' -- '%s'\n", keyname, newarg ) ); /* * Breakup keyname into a keysym and modifier flags. */ if( (keyname_nomods = STRRCHR( keyname, '+' )) == NULL ) { /* No modifiers specified */#ifdef UNSHIFTED_MACROS keyname_nomods = keyname;#else return -1;#endif } else { *(keyname_nomods++) = 0; /* * keyname is now a null terminated string containing only the * modifiers, and keyname_nomods is a null terminated string * containing only the key name. */ if( STRCASESTR( keyname, "ctrl" ) ) modFlags |= MACRO_CTRL; if( STRCASESTR( keyname, "meta" ) || STRCASESTR( keyname, "alt")) modFlags |= MACRO_META; if( STRCASESTR( keyname, "shift") ) modFlags |= MACRO_SHIFT; if( STRCASESTR( keyname, "primary")) modFlags |= MACRO_PRIMARY; if( STRCASESTR( keyname, "add" ) ) addmacro = True; } /* * Always store the keysym as lower case. That way we can treat shift * correctly even when Caps Lock is pressed. */ keysym = tolower( XStringToKeysym( keyname_nomods ) ); if( keysym == None ) { rxvt_print_error( "Invalid keysym %s. Skipping macro.", keyname_nomods); return -1; } return rxvt_add_macro( r, keysym, modFlags, newarg, addmacro, noReplace) ? 1 : -1;}/* {{{1 rxvt_add_macro( keysym, modFlags, astring, noreplace) * * Add a macro to our list of macros (r->macros) if astring describes a valid * macro. If keysym == None, then add the macro to the previous one. * * If noReplace is true, don't replace existing macros. Required when reading * the system config file so that we don't replace existing user macros. *//* INTPROTO */intrxvt_add_macro( rxvt_t *r, KeySym keysym, unsigned char modFlags, char *astring, Bool addmacro, Bool noReplace){ DBG_MSG( 2, ( stderr, "rxvt_add_macro(%08lx, %2hhx, %s)\n", keysym, modFlags, astring)); const unsigned nmacros_increment = 64; /* # extra macros to alloc space for when we need to enlarge our list of macros. A large number here is not wasteful as we clean it up after initialization */ unsigned short i, replaceIndex = r->nmacros, dummyIndex = r->nmacros; unsigned char macroNum = 0; action_t action; /* * Check to see if macro already exists. */ for( i=0; i < r->nmacros; i++ ) { if( r->macros[i].keysym == keysym && (r->macros[i].modFlags & MACRO_MODMASK & ~MACRO_PRIMARY) == (modFlags & MACRO_MODMASK & ~MACRO_PRIMARY) ) { if( addmacro ) { /* * Find the last macro in the macro chain (the macro with the * largest number). */ unsigned char num; num = MACRO_GET_NUMBER( r->macros[i].modFlags ); if( num > macroNum ) macroNum = num; if( macroNum == MACRO_MAX_CHAINLEN ) { rxvt_print_error( "Macro chain too long" ); return 0; } replaceIndex = i; } /* * Macro for key already exists. If noReplace is set, don't * touch this macro. */ else if( noReplace ) return 1; /* Claim to have succeded so that caller will not complain about "Failing to add a ... macro". */ /* * 2006-03-06 gi1242: Don't delete "Dummy" macros here. If we do * that then the user will have no way to delete macros defined in * the system /etc/mrxvt/mrxvtrc file. "Dummy" macros will be * deleted after init. */ else { if( replaceIndex < r->nmacros ) { /* * replaceIndex points to a macro with keysym == the keysym * of the macro to be added. Set keysym to 0 so that it will * be cleaned up by rxvt_cleanup_macros(). */ r->macros[replaceIndex].keysym = 0; } replaceIndex = i; } } else if( r->macros[i].keysym == 0 ) /* * Macros with keysym 0 are dummies, and can be safely replaced. */ dummyIndex = i; } /* for */ if( addmacro ) { if( replaceIndex == r->nmacros ) { rxvt_print_error( "No previous macro to add to (key %s%s%s%s)", (modFlags & MACRO_CTRL) ? "Ctrl+" : "", (modFlags & MACRO_META) ? "Meta+" : "", (modFlags & MACRO_SHIFT) ? "Shift+" : "", XKeysymToString( keysym ) ); return 0; /* Failure */ } modFlags = macro_set_number( modFlags, macroNum+1 ); /* Don't replace this macro. */ replaceIndex = dummyIndex; } else { modFlags = macro_set_number( modFlags, 0 ); /* Set replaceIndex to the index of a macro we can replace */ if( dummyIndex < replaceIndex ) replaceIndex = dummyIndex; } /* * Add action to the list of macros (making it bigger if necessary). */ if( replaceIndex == r->nmacros ) { if( r->nmacros == r->maxMacros ) { /* Get space for more macros*/ r->maxMacros += nmacros_increment; r->macros = (macros_t *) rxvt_realloc( r->macros, r->maxMacros * sizeof(macros_t)); } r->nmacros++; } else { /* Macro action string needs to be freed (as it will be replaced) */ free( r->macros[replaceIndex].action.str ); } /* * Set the action string. This malloc's memory so any returns after this * should either save action in to a global variable, or free it. */ assert( astring ); action.str = NULL; /* Make sure rxvt_set_action won't free non-existent memory */ if( !rxvt_set_action( &action, astring) ) return 0; /* Failure: Probably unrecognized action type */ /* * Save macro values in our global macro list. */ r->macros[replaceIndex].keysym = keysym; r->macros[replaceIndex].modFlags = modFlags; r->macros[replaceIndex].action = action; DBG_MSG( 2, ( stderr, "Added macro %hu of %hu. " "Type %s, len %hu, args '%s'.\n", replaceIndex, r->maxMacros, macroNames[ action.type ], action.len, (action.type == MacroFnStr || action.type == MacroFnEsc) ? "(escaped string)" : ((action.str == NULL) ? "(nil)" : (char*) action.str))); return 1; /* Success */}/* {{{1 rxvt_cleanup_macros() * * Delete all "Dummy" macros from our list of macros, and free space alloced for * extra macros */voidrxvt_cleanup_macros( rxvt_t *r ){ unsigned i, nDummyMacros = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -