📄 macros.c
字号:
return 0; /* Failure */ } else if( r->macros[replaceIndex].priority != priority ) { rxvt_msg( DBG_ERROR, DBG_MACROS, "Can not add to a macro defined at a different location " "(key %s%s%s%s)", (modFlags & MACRO_CTRL) ? "Ctrl+" : "", (modFlags & MACRO_META) ? "Meta+" : "", (modFlags & MACRO_SHIFT) ? "Shift+" : "", XKeysymToString( keysym ) ); return 0; /* Failure */ } else if( r->macros[replaceIndex].action.type == MacroFnDummy ) { /* Do not add to a dummy macro */ rxvt_msg (DBG_ERROR, DBG_MACROS, "Can not add actions to a Dummy macro" "(key %s%s%s%s)", (modFlags & MACRO_CTRL) ? "Ctrl+" : "", (modFlags & MACRO_META) ? "Meta+" : "", (modFlags & MACRO_SHIFT) ? "Shift+" : "", XKeysymToString( keysym ) ); return 0; /* Failure */ } /* * We're going to add to this macro chain, so don't replace this macro. */ replaceIndex = dummyIndex; /* * Make the number of this macro one higher than the last in the macro * chain. */ modFlags = macro_set_number( modFlags, macroNum+1 ); } 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) */ if( r->macros[replaceIndex].action.str ) rxvt_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 ); SET_NULL(action.str); /* 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; r->macros[replaceIndex].priority = priority; rxvt_dbgmsg(( DBG_DEBUG, DBG_MACROS, "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)" : (IS_NULL(action.str) ? "(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; if( r->nmacros == 0 ) return; /* Nothing to be done */ for( i = 0; i < r->nmacros; i++) { if( r->macros[i].action.type == MacroFnDummy || NOT_KEYSYM(r->macros[i].keysym) ) { /* * Dummy macro needs to be deleted. Make sure this macro comes first * in the macro list. * * 2006-03-06 gi1242: Would be more efficient if we made sure that * this macro was last in the list, however that would involve * knowing what the max keysym value is. Could be different on * different architectures. */ r->macros[i].keysym = 0; r->macros[i].modFlags = 0; if (NOT_NULL(r->macros[i].action.str)) { rxvt_free( r->macros[i].action.str ); SET_NULL(r->macros[i].action.str); /* Probably unnecessary */ } nDummyMacros++; } } /* for */ /* * The macro list now needs to be sorted on keysym. When we look for macros, * we assume the macro list is sorted, so we can use a binary search to * lookup macros quickly. */ qsort( r->macros, r->nmacros, sizeof( macros_t ), macro_cmp); /* Remove dummy macros from our list */ MEMMOVE( r->macros, r->macros + nDummyMacros, (r->nmacros - nDummyMacros) * sizeof( macros_t ) ); r->nmacros -= nDummyMacros; /* Shrink our macros list */ if( r->nmacros < r->maxMacros ) { r->macros = rxvt_realloc( r->macros, r->nmacros * sizeof( macros_t )); r->maxMacros = r->nmacros; } rxvt_dbgmsg(( DBG_DEBUG, DBG_MACROS, "Read %d macros. (Have space for %d macros)\n", r->nmacros, r->maxMacros));}/* {{{1 rxvt_set_action( action, astring) * * Check what action is specified by astring, and assign respective values in * action. * * The string astring might be modified, but can be freed immediately after * calling this function (regardless of wether it succeeds or not). *//* EXTPROTO */Boolrxvt_set_action (action_t *action, char *astring){ unsigned short type, len; rxvt_dbgmsg ((DBG_DEBUG, DBG_MACROS, "Setting action '%s'\n", astring)); /* * Match head of "astring" to a name in macroNames to figure out the macro * type. */ for( type = 0; type < NMACRO_FUNCS; type++) { if( (len = rxvt_str_match( astring, macroNames[type])) ) { /* Matched a macroName at the start of astring */ if( astring[len] && !isspace( astring[len] ) ) /* Not delimited by a space */ continue; /* Skip macroName and delimiting spaces */ astring += len; while( *astring && isspace( *astring ) ) astring++; /* Exit for loop early */ break; } } if( type == NMACRO_FUNCS ) { rxvt_msg (DBG_ERROR, DBG_MACROS, "Action %s is not of known type", astring); return False; /* Failure: No matching macro name */ } /* * Setup values in action */ action->type = type; /* * Interpolate escape sequences into action. XXX: Should we only do this for * MacroFnStr and MacroFnEsc?. */ len = rxvt_str_escaped( astring ); /* All macros exept MacroFnStr and MacroFnEsc have null terminated string */ if( type != MacroFnStr && type != MacroFnEsc && len > 0 && astring[len-1] ) astring[ len++ ] = 0; /* Since astring was null terminated, astring[len] is certainly part of the memory in astring. */ action->len = len; /* Set action->str. If any data is previously there, realloc it. */ if( len > 0 ) { action->str = (unsigned char *) rxvt_realloc( action->str, len * sizeof(unsigned char)); MEMCPY( action->str, astring, len); } else if (NOT_NULL(action->str)) { rxvt_free( action->str ); SET_NULL(action->str); } return True;}/* {{{1 rxvt_process_macros( keysym, ev) * * Check to see if a macro key was pressed. If yes, exec the action and return * 1. Else return 0. * * 2006-02-24 gi1242: Take both a keysym, and a XKeyEvent argument because, the * caller might have modified keysym based on XIM. *//* EXTPROTO */intrxvt_process_macros( rxvt_t *r, KeySym keysym, XKeyEvent *ev){ macros_t ck, /* Storing the keysym and mods of the current key that's pressed. */ *macro; /* Macro we find in our saved list corresponding to the current key press */ int status; if( r->nmacros == 0 ) return 0; /* No macro processed */ /* Copy the modifier mask and keysym into ck */ ck.modFlags = 0; if (ev->state & ShiftMask) ck.modFlags |= MACRO_SHIFT; if (ev->state & ControlMask) ck.modFlags |= MACRO_CTRL; if (ev->state & r->h->ModMetaMask) ck.modFlags |= MACRO_META; /* Use uppercase version so we can ignore caps lock */ { KeySym upper; XConvertCase(keysym, &ck.keysym, &upper); } /* Check if macro ck is in our list of macros. */ macro = bsearch( &ck, r->macros, r->nmacros, sizeof( macros_t ), macro_cmp); if ( /* * No macro found. */ IS_NULL(macro) || ( /* * Primary only macro in secondary screen. */ (macro->modFlags & MACRO_PRIMARY) && AVTS(r)->current_screen != PRIMARY ) || ( /* * When macros are disabled, only the toggle macros macro should * work. */ ISSET_OPTION(r, Opt2_disableMacros) && macro->action.type != MacroFnToggleMacros ) ) return 0; /* No macro processed */ do { rxvt_dbgmsg ((DBG_DEBUG, DBG_MACROS, "Processing macro #%d mods %02hhx\n", macro - r->macros, macro->modFlags)); status = rxvt_dispatch_action( r, &(macro->action), (XEvent*) ev ); } while( status == 1 && (++macro - r->macros) < r->nmacros && MACRO_GET_NUMBER( macro->modFlags ) ); return status;}/* {{{1 rxvt_dispatch_action( action, ev) * * Exec the macro / menu action with type "type" and action "action". Returns 1 * on success, -1 on failure. *//* EXTPROTO */intrxvt_dispatch_action( rxvt_t *r, action_t *action, XEvent *ev){ const int maxLen = 1024; char expstr[ maxLen ]; char *astr; int alen, retval = 1; /* Succeed by default */ if( IS_NULL( action->str ) ) { SET_NULL( astr ); alen = 0; } else { /* % interpolate the action string */ astr = expstr; alen = rxvt_percent_interpolate( r, ATAB(r), (char *) action->str, action->len, astr, maxLen ); } switch( action->type ) { case MacroFnEsc: /* Send action to rxvt */ if( NOT_NULL( astr ) && alen > 1 ) rxvt_cmd_write( r, ATAB(r), (unsigned char*) astr, alen - 1); else { rxvt_msg (DBG_ERROR, DBG_MACROS, "Macro %s requires argument.", macroNames[action->type] ); retval = -1; } break; case MacroFnStr: /* Send action to child process */ if( NOT_NULL( astr ) && alen > 1 ) rxvt_tt_write( r, ATAB(r), (unsigned char*) astr, alen - 1); else { rxvt_msg (DBG_ERROR, DBG_MACROS, "Macro %s requires argument.", macroNames[action->type] ); retval = -1; } break; case MacroFnNewTab: if (NOT_NULL(astr)) { /* * If the first word is quoted, use that as the title. Don't be * fancy and check for nested quotes. That's probably * unnecessary. * * Everything after the first quoted word is the command. If * command starts with "!", then the shell is exec'ed before * running command. */ const int MaxMacroTitle = 80; /* Longest title we will have */ char titlestring[MaxMacroTitle]; char *command = (char *) astr; char *title = NULL; int profile = 0; /* See if a profile is specified */ if( *command == '-' ) { char *pnum_end; profile = strtoul( ++command, &pnum_end, 0 ); if( profile < 0 || profile >= MAX_PROFILES ) profile = AVTS(r)->profileNum; /* Skip spaces */ command = pnum_end; while( isspace( *command ) ) command++; } /* See if a title is specified */ if( *command == '"' ) { int i; /* Copy everything until first " into title */ for( i=0, command++; i < MaxMacroTitle - 2 && *command && *command != '"'; i++, command++ ) titlestring[i] = *command; titlestring[i] = '\0'; /* Null terminate title */ title = titlestring; /* Skip spaces after title */ if( *command ) command++; while( isspace( *command ) ) command++; } /* Add page */ rxvt_append_page( r, profile, title, *command ? command : NULL ); } else rxvt_append_page( r, 0, NULL, NULL ); break; case MacroFnExec: if( NOT_NULL( astr ) ) retval = rxvt_async_exec( r, astr ) ? 1 : -1; else { rxvt_msg (DBG_ERROR, DBG_MACROS, "Macro %s requires argument.", macroNames[action->type] ); retval = -1; } break; case MacroFnClose: if( alen > 0 && *(astr) ) { /* Close tab specified by str */ int tabno = atoi( (char*) astr) - 1; if( tabno == -1 ) tabno = ATAB(r); if ( tabno >=0 && tabno <=LTAB(r) && ( NOTSET_OPTION(r, Opt2_protectSecondary) || PVTS(r, tabno)->current_screen == PRIMARY ) ) { rxvt_kill_page (r, tabno); } else retval = -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -