1/* 2 * Copyright (c) 2008 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 <string.h> 26#include <assert.h> 27#include <curses.h> 28#include "libtop.h" 29#include "generic.h" 30#include "log.h" 31#include "top.h" 32 33enum { HEADER_SIZE = 1 }; 34 35int generic_draw_header(struct statistic *s, int x, int y, int anchor) { 36 if(s->header) { 37 generic_draw_extended(s, x, y, anchor, s->header, strlen(s->header)); 38 y += HEADER_SIZE; 39 } 40 41 return y; 42} 43 44void generic_draw_aligned(struct statistic *s, int x) { 45 int y = 0; 46 struct generic_cells *cells; 47 int peaklength = 0; 48 int maxy; 49 size_t i; 50 51 cells = s->cells; 52 53 if(NULL == cells) 54 return; 55 56 /* This is needed for panels. */ 57 werase(s->window); 58 59 y = generic_draw_header(s, x, y, GENERIC_DRAW_LEFT); 60 61 maxy = s->actual_size.height; 62 63 peaklength = s->actual_size.width; 64 65 for(i = 0; i < cells->length && y < maxy; ++i, ++y) { 66 int len = cells->array[i].length; 67 int localx; 68 69 localx = peaklength - len; 70 71 if(len <= 0) 72 continue; 73 74 if(ERR == mvwaddnstr(s->window, y, localx, 75 cells->array[i].string, len)) { 76 int erry, errx; 77 getmaxyx(s->window, erry, errx); 78 79 top_log("mvwaddnstr error in %s\n", __func__); 80 top_log("error info: s->header %s y %d x %d n %d string: %s\n" 81 "s->window width %d height %d\n", 82 s->header, y, localx + x, len, 83 cells->array[i].string, 84 errx, erry); 85 } 86 } 87} 88 89void generic_draw(struct statistic *s, int x) { 90 int y = 0; 91 size_t i; 92 struct generic_cells *cells; 93 int maxy; 94 95 cells = s->cells; 96 97 if(NULL == cells) 98 return; 99 100 /* This is needed for panels. */ 101 werase(s->window); 102 103 y = generic_draw_header(s, x, y, GENERIC_DRAW_LEFT); 104 105 maxy = s->actual_size.height; 106 107 for(i = 0; i < cells->length && y < maxy; ++i, ++y) { 108 generic_draw_extended(s, x, y, GENERIC_DRAW_LEFT, 109 cells->array[i].string, 110 cells->array[i].length); 111 } 112} 113 114/* 115 * This is meant to be called by a statistic draw function. 116 * It's not directly for use in a callback. 117 */ 118void generic_draw_extended(struct statistic *s, int xoffset, int yoffset, int anchor, const char *string, int slen) { 119 int yheight, xwidth, xpos; 120 int n; 121 122 xwidth = s->actual_size.width; 123 yheight = s->actual_size.height; 124 125 switch(anchor) { 126 case GENERIC_DRAW_LEFT: 127 n = xwidth - xoffset; 128 if(n > slen) 129 n = slen; 130 131 mvwaddnstr(s->window, yoffset, xoffset, string, n); 132 break; 133 134 case GENERIC_DRAW_CENTERED: 135 /* Find the middle, and then subtract half of the string. */ 136 xpos = ((xwidth - xoffset) / 2) - (slen / 2); 137 138 if(xpos < 0) { 139 /* 140 * The string resulted in an offset less than the requested 141 * offset. We may need to truncate it later. 142 */ 143 xpos = 0; 144 } 145 146 n = slen; 147 148 if((slen + xpos) >= xwidth) { 149 /* The n chars is too large for this stat width. */ 150 n = xwidth - xpos - xoffset; 151 } 152 153 if(n < 0) { 154 xpos = 0; 155 n = 0; 156 } 157 158 mvwaddnstr(s->window, yoffset, xpos + xoffset, string, n); 159 break; 160 161 case GENERIC_DRAW_RIGHT: 162 xpos = xwidth - slen - xoffset; 163 164 if(xpos < xoffset) { 165 /* The string is too big to fit. */ 166 xpos = xoffset; 167 } 168 169 n = xwidth - xpos; 170 if(n > slen) 171 n = slen; 172 173 mvwaddnstr(s->window, yoffset, xpos + xoffset, string, n); 174 break; 175 } 176 177 wsyncup(s->window); 178} 179 180void generic_draw_centered(struct statistic *s, int x) { 181 struct generic_cells *cells; 182 size_t i; 183 int y = 0; 184 185 cells = s->cells; 186 187 if(NULL == cells) 188 return; 189 190 y = generic_draw_header(s, x, y, GENERIC_DRAW_CENTERED); 191 192 for(i = 0; i < cells->length; ++i) { 193 generic_draw_extended(s, x, y, GENERIC_DRAW_CENTERED, 194 cells->array[i].string, 195 cells->array[i].length); 196 ++y; 197 } 198} 199 200void generic_draw_right(struct statistic *s, int x) { 201 struct generic_cells *cells; 202 size_t i; 203 int y = 0; 204 205 cells = s->cells; 206 207 if(NULL == cells) 208 return; 209 210 y = generic_draw_header(s, x, y, GENERIC_DRAW_RIGHT); 211 212 for(i = 0; i < cells->length; ++i) { 213 generic_draw_extended(s, x, y, GENERIC_DRAW_RIGHT, 214 cells->array[i].string, 215 cells->array[i].length); 216 ++y; 217 } 218} 219 220bool generic_resize_cells(struct statistic *s, struct statistic_size *size) { 221 if(ERR == wresize(s->window, size->height, size->width)) 222 return true; 223 224 return false; 225} 226 227bool generic_move_cells(struct statistic *s, int x, int y) { 228 if(ERR == move_panel(s->panel, y, x)) 229 return true; 230 231 return false; 232} 233 234void generic_get_request_size(struct statistic *s) { 235 struct generic_cells *cells; 236 237 cells = s->cells; 238 239 if(NULL == cells) 240 return; 241 242 s->request_size.width = cells->max_width; 243 s->request_size.height = cells->length; 244} 245 246static struct generic_cells *alloc_generic_cells(void) { 247 size_t i, length = 10; 248 struct generic_cells *cells; 249 250 cells = malloc(sizeof *cells); 251 if(NULL == cells) 252 return NULL; 253 254 cells->array = malloc(sizeof(*(cells->array)) * length); 255 if(NULL == cells->array) { 256 free(cells); 257 return NULL; 258 } 259 260 for(i = 0; i < length; ++i) { 261 cells->array[i].string = NULL; 262 cells->array[i].length = 0; 263 cells->array[i].allocated_length = 0; 264 } 265 266 cells->max_width = 0; 267 cells->length = 0; 268 cells->length_allocated = length; 269 270 return cells; 271} 272 273static void free_generic_cells(struct generic_cells *cells) { 274 size_t i; 275 276 for(i = 0; i < cells->length_allocated; ++i) { 277 if(cells->array[i].string) { 278 free(cells->array[i].string); 279 } 280 } 281 282 free(cells->array); 283 free(cells); 284} 285 286static void generic_cell_destructor(struct statistic *s, void *ptr) { 287 free_generic_cells(s->cells); 288} 289 290/* Return true if an error occurred. */ 291bool generic_insert_cell(struct statistic *s, const char *sample) { 292 struct generic_cells *cells = s->cells; 293 size_t offset; 294 int sample_length = (int)strlen(sample); 295 int sample_z_length = sample_length + 1; 296 297#if 0 298 top_log("%s %s\n", __func__, sample); 299#endif 300 301 if(NULL == cells) { 302 cells = s->cells = alloc_generic_cells(); 303 304 if(NULL == cells) 305 return true; 306 307 if(create_statistic_destructor(s, generic_cell_destructor, NULL)) 308 return true; 309 } 310 311 312 /* Update the max_width if the sample_length is greater. */ 313 if(sample_length > cells->max_width) { 314 cells->max_width = sample_length; 315 316 if(cells->max_width > s->actual_size.width) { 317 /* This requests a top_layout at a later time. */ 318 top_relayout(s->controller, s->type, cells->max_width); 319 } 320 } 321 322 offset = cells->length; 323 324 cells->length += 1; 325 326 if(cells->length >= cells->length_allocated) { 327 /* Resize the array to store the length and more. */ 328 329 size_t newlength = cells->length_allocated * 2; 330 size_t i; 331 cells->array = realloc(cells->array, 332 sizeof(*(cells->array)) * newlength); 333 334 if(NULL == cells->array) 335 return true; 336 337 for(i = cells->length_allocated; i < newlength; ++i) { 338 cells->array[i].string = NULL; 339 cells->array[i].length = 0; 340 cells->array[i].allocated_length = 0; 341 } 342 343 cells->length_allocated = newlength; 344 } 345 346 if(0 == sample_length) { 347 cells->array[offset].length = 0; 348 return false; 349 } 350 351 if(cells->array[offset].string 352 && cells->array[offset].allocated_length >= sample_z_length) { 353 /* We have an existing buffer that should fit. */ 354 memcpy(cells->array[offset].string, sample, sample_z_length); 355 cells->array[offset].length = sample_length; 356 } else { 357 /* There wasn't enough space or the string was NULL. */ 358 free(cells->array[offset].string); 359 360 cells->array[offset].string = malloc(cells->max_width + 1); 361 362 if(NULL == cells->array[offset].string) { 363 cells->array[offset].length = 0; 364 return true; 365 } 366 367 cells->array[offset].length = sample_length; 368 cells->array[offset].allocated_length = cells->max_width + 1; 369 memcpy(cells->array[offset].string, sample, sample_z_length); 370 } 371 372 return false; 373} 374 375void generic_reset_insertion(struct statistic *s) { 376 struct generic_cells *cells; 377 378 cells = s->cells; 379 if(NULL == cells) 380 return; 381 382 cells->length = 0; 383} 384 385void generic_get_minimum_size(struct statistic *s) { 386 struct generic_cells *cells; 387 388 cells = s->cells; 389 390 if(NULL == cells) 391 return; 392 393 394 s->minimum_size.width = cells->max_width; 395 s->minimum_size.height = cells->length; 396 397 if(s->minimum_size.width < 4) 398 s->minimum_size.width = 4; 399} 400