1/* 2 * Copyright (c) 2008, 2009 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23#include <stdbool.h> 24#include <stdlib.h> 25#include <ctype.h> 26#include <string.h> 27#include <curses.h> 28#include <fcntl.h> 29#include <errno.h> 30#include <sys/types.h> 31#include <sys/uio.h> 32#include <unistd.h> 33#include "preferences.h" 34#include "userinput.h" 35#include "userinput_mode.h" 36#include "userinput_sleep.h" 37#include "userinput_order.h" 38#include "userinput_secondary_order.h" 39#include "userinput_user.h" 40#include "userinput_signal.h" 41#include "userinput_help.h" 42#include "top.h" 43 44static struct user_input_state *current_state = NULL; 45 46static void reset_state(struct user_input_state *s) { 47 s->buf[0] = '\0'; 48 s->offset = 0; 49} 50 51static void completion(void *tinst, struct user_input_state *s) { 52 reset_state(s); 53 current_state = NULL; 54} 55 56/* This displays an error for 1 draw interval. */ 57static void error_draw(void *tinst, struct user_input_state *s, WINDOW *win, 58 int row, int column) { 59 char display[60]; 60 61 if(s->misc > 1) { 62 s->completion(tinst, s); 63 return; 64 } 65 66 if(-1 == snprintf(display, sizeof(display), "error: %s", s->buf)) 67 return; 68 69 mvwaddstr(win, row, column, display); 70 71 s->misc++; 72} 73 74static struct user_input_state error_state = { 75 .offset = 0, 76 .completion = completion, 77 .draw = error_draw 78}; 79 80void user_input_set_error_state(const char *err) { 81 current_state = &error_state; 82 reset_state(current_state); 83 current_state->misc = 0; 84 strncpy(current_state->buf, err, sizeof(current_state->buf)); 85 current_state->buf[sizeof(current_state->buf) - 1] = '\0'; 86} 87 88/* This displays status output for 1 draw interval. */ 89static void status_draw(void *tinst, struct user_input_state *s, WINDOW *win, 90 int row, int column) { 91 if(s->misc > 1) { 92 s->completion(tinst, s); 93 return; 94 } 95 96 mvwaddstr(win, row, column, s->buf); 97 98 s->misc++; 99} 100 101 102static struct user_input_state status_state = { 103 .offset = 0, 104 .completion = completion, 105 .draw = status_draw 106}; 107 108void user_input_set_status_state(const char *status) { 109 current_state = &status_state; 110 reset_state(current_state); 111 current_state->misc = 0; 112 strncpy(current_state->buf, status, sizeof(current_state->buf)); 113 current_state->buf[sizeof(current_state->buf) - 1] = '\0'; 114} 115 116 117void user_input_set_state(struct user_input_state *state) { 118 current_state = state; 119} 120 121/* Process stdin data and return true if any was processed. */ 122bool user_input_process(void *tinst) { 123 124 if(&error_state == current_state) { 125 /* Sleep a bit in the error state, before switching. */ 126 usleep(500000); 127 return false; 128 } 129 130 if(&status_state == current_state) { 131 /* Return until the status state has completed its display. */ 132 return false; 133 } 134 135 int c = getch(); 136 137 if(ERR == c) { 138 int flags, modflags, readerrno = 0; 139 char tmp; 140 ssize_t r; 141 142 /* Get stdin's flags. */ 143 flags = fcntl(STDIN_FILENO, F_GETFL, 0); 144 145 /* Make stdin non-blocking. */ 146 modflags = flags | O_NONBLOCK; 147 148 if(-1 == fcntl(STDIN_FILENO, F_SETFL, modflags)) { 149 perror("fcntl"); 150 exit(EXIT_FAILURE); 151 } 152 153 r = read(STDIN_FILENO, &tmp, sizeof(tmp)); 154 155 readerrno = errno; 156 157 if(0 == r) { 158 /* An EOF occurred, so top should exit. */ 159 exit(EXIT_FAILURE); 160 } 161 162 /* Restore stdin's old flags. */ 163 if(-1 == fcntl(STDIN_FILENO, F_SETFL, flags)) { 164 perror("fcntl"); 165 exit(EXIT_FAILURE); 166 } 167 168 if(-1 == r && EAGAIN == readerrno) { 169 /* 170 * The read above returned an error, but it was due to the 171 * O_NONBLOCK with no available data. 172 */ 173 return false; 174 } 175 176 /* Set the character we read. */ 177 c = tmp; 178 179 /* Fall through and handle the character. */ 180 } 181 182 if(current_state) { 183 if(/*del*/ 127 == c) { 184 if(current_state->offset > 0) { 185 current_state->offset--; 186 current_state->buf[current_state->offset] = '\0'; 187 } 188 } else if('\r' == c) { 189 current_state->buf[current_state->offset] = '\0'; 190 current_state->completion(tinst, current_state); 191 192 /* 193 * The user changing an interactive option triggers the display 194 * of new data. 195 */ 196 top_insert(tinst); 197 top_draw(tinst); 198 } else { 199 if(current_state->offset < (sizeof(current_state->buf) - 2)) { 200 current_state->buf[current_state->offset++] = c; 201 current_state->buf[current_state->offset] = '\0'; 202 } 203 } 204 205 return true; 206 } 207 208 switch(c) { 209 case '?': 210 user_input_set_state(&top_user_input_help_state); 211 reset_state(current_state); 212 break; 213 214 case 'c': 215 user_input_set_state(&top_user_input_mode_state); 216 reset_state(current_state); 217 break; 218 219 case 'o': 220 user_input_set_state(&top_user_input_order_state); 221 reset_state(current_state); 222 break; 223 224 case 'O': 225 user_input_set_state(&top_user_input_secondary_order_state); 226 reset_state(current_state); 227 break; 228 229 case 'r': 230 top_prefs_set_mmr(!top_prefs_get_mmr()); 231 232 user_input_set_status_state(top_prefs_get_mmr() ? 233 "Report process memory object maps." 234 : 235 "Do not report process memory object maps."); 236 break; 237 238 case 's': 239 user_input_set_state(&top_user_input_sleep_state); 240 reset_state(current_state); 241 break; 242 243 case 'S': 244 user_input_set_state(&top_user_input_signal_state); 245 reset_state(current_state); 246 break; 247 248 case 'U': 249 user_input_set_state(&top_user_input_user_state); 250 reset_state(current_state); 251 break; 252 253 case 'q': 254 case 'Q': 255 exit(EXIT_SUCCESS); 256 break; 257 258 default: 259 if(current_state) { 260 reset_state(current_state); 261 return true; 262 } 263 } 264 265 if(/*^L*/ '\x0c' == c || /*space*/ ' ' == c || '\r' == c) { 266 /* Trigger a new insert regardless of the interval. */ 267 top_insert(tinst); 268 top_draw(tinst); 269 } 270 271 return true; 272} 273 274static int row, column; 275void user_input_set_position(int r, int c) { 276 row = r; 277 column = c; 278} 279 280void user_input_draw(void *tinst, WINDOW *win) { 281 if(current_state && current_state->draw) { 282 current_state->draw(tinst, current_state, win, row, column); 283 } 284} 285