/*
* screen.c
*
* Written By Matthew Green, based on portions of window.c
* by Michael Sandrof.
*
* Copyright (c) 1990 Michael Sandrof.
* Copyright (c) 1993-2006 Matthew R. Green.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "irc.h"
IRCII_RCSID("@(#)$eterna: screen.c,v 1.151 2006/07/22 03:50:10 mrg Exp $");
#ifdef HAVE_SYS_UN_H
# include <sys/un.h>
#endif /* HAVE_SYS_UN_H */
#ifdef HAVE_ICONV_H
# include <iconv.h>
#endif /* HAVE ICONV_H */
#include "screen.h"
#include "menu.h"
#include "window.h"
#include "output.h"
#include "vars.h"
#include "server.h"
#include "list.h"
#include "ircterm.h"
#include "names.h"
#include "ircaux.h"
#include "input.h"
#include "log.h"
#include "hook.h"
#include "dcc.h"
#include "translat.h"
#include "exec.h"
#include "newio.h"
#include "parse.h"
#include "edit.h"
#include "strsep.h"
#ifdef lines
#undef lines
#undef columns
#endif /* lines */
Window *to_window;
Screen *current_screen;
Screen *main_screen;
Screen *last_input_screen;
extern int in_help;
extern u_char *redirect_nick;
/* Our list of screens */
Screen *screen_list = NULL;
static void display_lastlog_lines(int, int, Window *);
static Screen * create_new_screen(void);
static u_char ** split_up_line(u_char *);
static void scrollback_backwards_lines(int);
static void scrollback_forwards_lines(int);
static u_char display_highlight(int);
static u_char display_bold(int);
static void display_colours(int, int);
static void display_text(u_char *, size_t);
static void decode_colour(u_char **, int *, int *);
static void add_to_window(Window *, u_char *);
static u_int create_refnum(void);
static void redraw_window(Window *, int, int);
static u_char *next_line_back(Window *);
static int lastlog_lines(Window *);
/*
* create_new_screen creates a new screen structure. with the help of
* this structure we maintain ircII windows that cross screen window
* boundaries.
*/
static Screen *
create_new_screen()
{
Screen *new = NULL,
**list;
static int refnumber = 0;
for (list = &screen_list; *list; list = &((*list)->next))
{
if (!(*list)->alive)
{
new = *list;
break;
}
}
if (!new)
{
new = (Screen *) malloc(sizeof(Screen));
new->screennum = ++refnumber;
new->next = screen_list;
if (screen_list)
screen_list->prev = new;
screen_list = new;
}
new->last_window_refnum = 1;
new->window_list = NULL;
new->window_list_end = NULL;
new->cursor_window = NULL;
new->current_window = NULL;
new->visible_windows = 0;
new->window_stack = NULL;
new->meta1_hit = new->meta2_hit = new->meta3_hit = new->meta4_hit = 0;
new->meta5_hit = new->meta6_hit = new->meta7_hit = new->meta8_hit = 0;
new->quote_hit = new->digraph_hit = new->inside_menu = 0;
new->fdout = 1;
new->fpout = stdout;
new->fdin = 0;
new->fpin = stdin;
new->alive = 1;
new->promptlist = NULL;
new->redirect_name = NULL;
new->redirect_token = NULL;
new->tty_name = (u_char *) 0;
new->li = main_screen ? main_screen->li : 24;
new->co = main_screen ? main_screen->co : 79;
new->old_term_li = -1;
new->old_term_co = -1;
input_reset_screen(new);
new->redirect_server = -1;
last_input_screen = new;
return new;
}
/*
* add_wait_prompt: Given a prompt string, a function to call when
* the prompt is entered.. some other data to pass to the function,
* and the type of prompt.. either for a line, or a key, we add
* this to the prompt_list for the current screen.. and set the
* input prompt accordingly.
*/
void
add_wait_prompt(prompt, func, data, type)
u_char *prompt;
void (*func)(u_char *, u_char *);
u_char *data;
int type;
{
WaitPrompt **AddLoc,
*New;
New = (WaitPrompt *) new_malloc(sizeof(WaitPrompt));
New->prompt = NULL;
malloc_strcpy(&New->prompt, prompt);
New->data = NULL;
malloc_strcpy(&New->data, data);
New->type = type;
New->func = func;
New->next = NULL;
for (AddLoc = ¤t_screen->promptlist; *AddLoc;
AddLoc = &(*AddLoc)->next);
*AddLoc = New;
if (AddLoc == ¤t_screen->promptlist)
change_input_prompt(1);
}
void
set_current_screen(screen)
Screen *screen;
{
if (screen->alive)
current_screen = screen;
else
current_screen = screen_list;
term_set_fp(current_screen->fpout);
}
/*
* window_redirect: Setting who to non null will cause IRCII to echo all
* commands and output from that server (including output generated by IRCII)
* to who. Setting who to null disables this
*/
void
window_redirect(who, server)
u_char *who;
int server;
{
u_char buf[BIG_BUFFER_SIZE];
if (who)
snprintf(CP(buf), sizeof buf, "%04d%s", server, who);
else
snprintf(CP(buf), sizeof buf, "%04d#LAME", server);
malloc_strcpy(¤t_screen->redirect_token, buf);
malloc_strcpy(¤t_screen->redirect_name, who);
current_screen->redirect_server = server;
}
int
check_screen_redirect(nick)
u_char *nick;
{
Screen *screen,
*tmp_screen;
for (screen = screen_list; screen; screen = screen->next)
{
if (!screen->redirect_token)
continue;
if (!my_strcmp(nick, screen->redirect_token))
{
tmp_screen = current_screen;
set_current_screen(screen);
window_redirect(NULL, from_server);
set_current_screen(tmp_screen);
return 1;
}
}
return 0;
}
/* Old screens never die. They just fade away. */
#ifdef WINDOW_CREATE
void
kill_screen(screen)
Screen *screen;
{
Window *window;
if (main_screen == screen)
{
say("You may not kill the main screen");
return;
}
if (screen->fdin)
{
new_close(screen->fdout);
new_close(screen->fdin);
}
while ((window = screen->window_list))
{
screen->window_list = window->next;
add_to_invisible_list(window);
}
if (last_input_screen == screen)
last_input_screen = screen_list;
screen->alive = 0;
}
int
is_main_screen(screen)
Screen *screen;
{
return (screen == main_screen);
}
#else
static int
is_main_screen(screen)
Screen *screen;
{
return 1;
}
#endif /* WINDOW_CREATE */
/*
* scroll_window: Given a pointer to a window, this determines if that window
* should be scrolled, or the cursor moved to the top of the screen again, or
* if it should be left alone.
*/
void
scroll_window(window)
Window *window;
{
if (dumb)
return;
if (window->cursor == window->display_size)
{
if (window->scroll)
{
int do_scroll,
i;
if ((do_scroll = get_int_var(SCROLL_LINES_VAR)) <= 0)
do_scroll = 1;
for (i = 0; i < do_scroll; i++)
{
new_free(&window->top_of_display->line);
window->top_of_display =
window->top_of_display->next;
}
if (window->visible)
{
if (term_scroll(window->top + window->menu.lines, window->top + window->menu.lines + window->cursor - 1, do_scroll))
{
if (current_screen->visible_windows == 1)
{
Window *tmp;
/*
* this method of sim-u-scroll seems to work fairly
* well. The penalty is that you can only have one
* window, and of course you can't use the scrollback
* buffer either. Or menus. Actually, this method
* really doesn't work very well at all anymore.
*/
tmp = current_screen->cursor_window;
term_move_cursor(0, current_screen->li - 2);
if (term_clear_to_eol())
term_space_erase(0);
term_cr();
term_newline();
for (i = 0; i < do_scroll; i++)
{
term_cr();
term_newline();
}
update_window_status(window, 1);
update_input(UPDATE_ALL);
current_screen->cursor_window = tmp;
Debug((3, "scroll_window: screen %d cursor_window set to window %d (%d)", current_screen->screennum, window->refnum, window->screen->screennum));
}
else
redraw_window(window, 1, 0);
}
window->cursor -= do_scroll;
term_move_cursor(0, window->cursor + window->top + window->menu.lines);
}
else
window->cursor -= do_scroll;
}
else
{
window->cursor = 0;
if (window->visible)
term_move_cursor(0, window->top + window->menu.lines);
}
}
else if (window->visible && current_screen->cursor_window == window)
{
term_cr();
term_newline();
}
if (window->visible && current_screen->cursor_window)
{
if (term_clear_to_eol()) /* && !window->hold_mode && !window->hold_on_next_rite) */
{
term_space_erase(0);
term_cr();
}
term_flush();
}
}
/* display_highlight: turns off and on the display highlight. */
static u_char
display_highlight(flag)
int flag;
{
static int highlight = OFF;
term_flush();
if (flag == highlight)
return (flag);
switch (flag)
{
case ON:
highlight = ON;
if (get_int_var(INVERSE_VIDEO_VAR))
term_standout_on();
return (OFF);
case OFF:
highlight = OFF;
if (get_int_var(INVERSE_VIDEO_VAR))
term_standout_off();
return (ON);
case TOGGLE:
if (highlight == ON)
{
highlight = OFF;
if (get_int_var(INVERSE_VIDEO_VAR))
term_standout_off();
return (ON);
}
else
{
highlight = ON;
if (get_int_var(INVERSE_VIDEO_VAR))
term_standout_on();
return (OFF);
}
}
return flag;
}
/* display_bold: turns off and on the display bolding. */
static u_char
display_bold(flag)
int flag;
{
static int bold = OFF;
term_flush();
if (flag == bold)
return (flag);
switch (flag)
{
case ON:
bold = ON;
if (get_int_var(BOLD_VIDEO_VAR))
term_bold_on();
return (OFF);
case OFF:
bold = OFF;
if (get_int_var(BOLD_VIDEO_VAR))
term_bold_off();
return (ON);
case TOGGLE:
if (bold == ON)
{
bold = OFF;
if (get_int_var(BOLD_VIDEO_VAR))
term_bold_off();
return (ON);
}
else
{
bold = ON;
if (get_int_var(BOLD_VIDEO_VAR))
term_bold_on();
return (OFF);
}
}
return OFF;
}
/* display_colours sets the foreground and background colours of the display
*/
static void
display_colours(fgcolour, bgcolour)
int fgcolour;
int bgcolour;
{
if (get_int_var(COLOUR_VAR))
{
/* Some people will say that I should use termcap values but
* since:
* 1- iso 6429 is the only used way for colour in the unix
* realm for now
* 2- colour information in termcap entries is still rare
* 3- information about colour information in termcap entries
* is still rare too
* ... I'll stick with this way for now. But having only 8-9
* colour is a pity.
* -- Sarayan
*/
/* Written by Bisqwit (bisqwit@iki.fi) */
/* mirc colours -> iso 6469 colours translation tables */
static const u_char trans[] = "7042115332664507";
static const u_char bolds[] = "1000100011011110";
/* 0123456789ABCDEF */
u_char iso[15]; /* long enough for "e[0;1;5;37;40m" */
snprintf(CP(iso), sizeof iso, "\33[0;");
if (bolds[fgcolour] == '1')
my_strcat(iso, "1;");
if (bolds[bgcolour] == '1')
my_strcat(iso, "5;");
snprintf(CP(my_index(iso, 0)), 7, "3%c;4%cm", trans[fgcolour&15], trans[bgcolour&15]);
fwrite(CP(iso), my_strlen(iso), 1, current_screen->fpout);
}
}
static void
display_text(ustr, length)
u_char *ustr;
size_t length;
{
iconv_const char *str = (iconv_const char *)ustr;
if (length > 0)
{
#ifdef HAVE_ICONV_OPEN
static iconv_t converter = NULL;
if (display_encoding)
{
if (!str)
{
/* str = NULL means reinitialize iconv. */
if (converter)
{
iconv_close(converter);
converter = NULL;
}
return;
}
if (!converter)
{
converter = iconv_open(CP(display_encoding), "UTF-8");
if (converter == (iconv_t)(-1))
{
iconv_close(converter);
converter = NULL;
}
}
}
if (converter)
{
char final = 0;
while (!final)
{
char OutBuf[512], *outptr = OutBuf;
size_t outsize = sizeof OutBuf;
size_t retval = 0;
if (length <= 0)
{
/* Reset the converter, create a reset-sequence */
retval = iconv(converter,
NULL, &length,
&outptr, &outsize);
final = 1;
}
else
{
retval = iconv(converter,
&str, &length,
&outptr, &outsize);
}
/* Write out as much as we got */
fwrite(OutBuf, sizeof(OutBuf)-outsize, 1, current_screen->fpout);
if (retval == (size_t)-1)
{
if (errno == E2BIG)
{
/* Outbuf could not contain everything. */
/* Try again with a new buffer. */
continue;
}
if (errno == EILSEQ)
{
/* Ignore 1 illegal byte silently. */
if (length > 0)
{
++str;
--length;
}
}
if (errno == EINVAL)
{
/* Input was terminated with a partial byte. */
/* Ignore the error silently. */
length = 0;
}
}
}
return;
}
#endif /* HAVE_ICONV_OPEN */
/* No usable iconv, assume output must be ISO-8859-1 */
if (str != NULL)
{
char OutBuf[1024], *outptr=OutBuf;
/* Convert the input to ISO-8859-1 character
* by character, flush to fpout in blocks.
* Ignore undisplayable characters silently.
*/
while (*str != '\0' && length > 0)
{
unsigned len = calc_unival_length(UP(str));
unsigned unival;
if (!len)
{
/* ignore illegal byte (shouldn't happen) */
++str;
continue;
}
if (len > length)
break;
length -= len;
unival = calc_unival(UP(str));
if (displayable_unival(unival, NULL))
{
if (outptr >= OutBuf+sizeof(OutBuf))
{
/* flush a block */
fwrite(OutBuf, outptr-OutBuf, 1,
current_screen->fpout);
outptr = OutBuf;
}
*outptr++ = unival;
}
str += len;
}
if (outptr > OutBuf)
{
/* Flush the last block */
fwrite(OutBuf, outptr-OutBuf, 1,
current_screen->fpout);
}
}
}
}
static void
display_nonshift(void)
{
display_text(NULL, 1);
}
/*
* output_line prints the given string at the current screen position,
* performing adjustments for ^_, ^B, ^V, and ^O
* If the input is longer than line may be, it cuts it.
* Return value: Number of columns printed
*/
int
output_line(str, startpos)
u_char *str;
int startpos;
{
int fgcolour_user = get_int_var(FOREGROUND_COLOUR_VAR),
bgcolour_user = get_int_var(BACKGROUND_COLOUR_VAR);
static int high = OFF,
bold = OFF,
fgcolour = -1,
bgcolour = -1;
int rev_tog, und_tog, bld_tog, all_off;
int dobeep = 0;
int written = 0;
display_highlight(high);
display_bold(bold);
display_colours(fgcolour, bgcolour);
/* do processing on the string, handle inverse and bells */
display_nonshift();
while (*str)
{
switch (*str)
{
case REV_TOG:
case UND_TOG:
case BOLD_TOG:
case ALL_OFF:
display_nonshift();
rev_tog = und_tog = bld_tog = all_off = 0;
switch (*str++)
{
case REV_TOG:
rev_tog = 1 - rev_tog;
break;
case UND_TOG:
und_tog = 1 - und_tog;
break;
case BOLD_TOG:
bld_tog = 1 - bld_tog;
break;
case ALL_OFF:
all_off = 1;
und_tog = rev_tog = bld_tog = 0;
break;
}
if (all_off)
{
if (!underline)
{
term_underline_off();
underline = 1;
}
display_highlight(OFF);
display_bold(OFF);
display_colours(
fgcolour = fgcolour_user,
bgcolour = bgcolour_user);
high = 0;
bold = 0;
}
if (und_tog && get_int_var(UNDERLINE_VIDEO_VAR))
{
/*
* Fix up after termcap may have turned
* everything off.
*/
if (bold)
display_bold(ON);
if (high)
display_highlight(ON);
if ((underline = 1 - underline) != 0)
term_underline_off();
else
term_underline_on();
}
if (rev_tog)
{
/*
* Fix up after termcap may have turned
* everything off.
*/
if (!underline)
term_underline_on();
if (bold)
display_bold(ON);
high = display_highlight(TOGGLE);
high = 1 - high;
}
if (bld_tog)
{
/*
* Fix up after termcap may have turned
* everything off.
*/
if (!underline)
term_underline_on();
if (high)
display_highlight(ON);
bold = display_bold(TOGGLE);
bold = 1 - bold;
}
break;
case COLOUR_TAG:
display_nonshift();
while (*str == COLOUR_TAG)
{
/* parse all consequent colour settings */
int fg, bg;
fg = *++str;
if (!fg--)
break;
bg = *++str;
if (!bg--)
break;
if (fg < 16)
fgcolour = fg;
if (bg < 16)
bgcolour = bg;
++str;
}
display_colours(fgcolour, bgcolour);
break;
case FULL_OFF:
++str;
display_nonshift();
if (!underline)
{
term_underline_off();
underline = 1;
}
display_highlight(OFF);
display_bold(OFF);
display_colours(
fgcolour = fgcolour_user,
bgcolour = bgcolour_user);
high = 0;
bold = 0;
/* fgcolour = bgcolour = 16; */
break;
case '\007':
/* After we display everything, we beep the terminal */
++dobeep;
++str;
break;
default:
{
unsigned n = calc_unival_length(str);
/* n should never be 0 (that would mean a broken
* character), but here we just ensure we
* don't get an infinite loop.
*/
if (n == 0)
n = 1;
/* Input is supposedly an UTF-8 character */
if (written < current_screen->co)
{
unsigned unival = calc_unival(str);
written += calc_unival_width(unival);
display_text(str, n);
}
str += n;
break;
}
}
}
display_nonshift();
if (dobeep)
term_beep();
return written;
}
/*
* rite: this routine displays a line to the screen adding bold facing when
* specified by ^Bs, etc. It also does handles scrolling and paging, if
* SCROLL is on, and HOLD_MODE is on, etc. This routine assumes that str
* already fits on one screen line. If show is true, str is displayed
* regardless of the hold mode state. If redraw is true, it is assumed we a
* redrawing the screen from the display_ip list, and we should not add what
* we are displaying back to the display_ip list again.
*
* Note that rite sets display_highlight() to what it was at then end of the
* last rite(). Also, before returning, it sets display_highlight() to OFF.
* This way, between susequent rites(), you can be assured that the state of
* bold face will remain the same and the it won't interfere with anything
* else (i.e. status line, input line).
*/
int
rite(window, str, show, redraw, backscroll, logged)
Window *window;
u_char *str;
int show,
redraw,
backscroll,
logged;
{
static int high = OFF;
Screen *old_current_screen = current_screen;
if (!redraw && !backscroll && window->scrolled_lines)
window->new_scrolled_lines++;
if (window->hold_mode && window->hold_on_next_rite && !redraw && !backscroll)
{
/* stops output from going to the window */
window->hold_on_next_rite = 0;
hold_mode(window, ON, 1);
if (show)
return (1);
}
/*
* Don't need to worry about the current_screen if the window isn't
* visible, as hidden windows aren't attached to a screen anyway
*/
if (window->visible)
{
old_current_screen = current_screen;
set_current_screen(window->screen);
}
if (!show && (hold_output(window) || hold_queue(window)) && !in_help && !redraw && !backscroll)
/* sends window output to the hold list for that window */
add_to_hold_list(window, str, logged);
else
{
if (!redraw && !backscroll)
{
/*
* This isn't a screen refresh, so add the line to the display
* list for the window
*/
if (window->scroll)
scroll_window(window);
malloc_strcpy(&(window->display_ip->line), str);
window->display_ip->linetype = logged;
window->display_ip = window->display_ip->next;
if (!window->scroll)
new_free(&window->display_ip->line);
}
if (window->visible)
{
int written;
/* make sure the cursor is in the appropriate window */
if (current_screen->cursor_window != window &&
!redraw && !backscroll)
{
current_screen->cursor_window = window;
Debug((3, "rite: screen %d cursor_window set to window %d (%d)", current_screen->screennum, window->refnum, window->screen->screennum));
term_move_cursor(0, window->cursor +
window->top + window->menu.lines);
}
written = output_line(str, 0);
if (term_clear_to_eol() && written < current_screen->co)
{
/* EOL wasn't implemented, so do it with spaces */
term_space_erase(current_screen->co - written);
}
}
else if (!(window->miscflags & WINDOW_NOTIFIED))
{
if ((who_level & window->notify_level)
|| ((window->notify_level & LOG_BEEP)
&& my_index(str, '\007')))
{
window->miscflags |= WINDOW_NOTIFIED;
if (window->miscflags & WINDOW_NOTIFY)
{
Window *old_to_window;
int lastlog_level;
lastlog_level =
set_lastlog_msg_level(LOG_CRAP);
old_to_window = to_window;
to_window = curr_scr_win;
say("Activity in window %d",
window->refnum);
to_window = old_to_window;
set_lastlog_msg_level(lastlog_level);
}
update_all_status();
}
}
if (!redraw && !backscroll)
{
window->cursor++;
window->line_cnt++;
if (window->scroll)
{
if (window->line_cnt >= window->display_size)
{
window->hold_on_next_rite = 1;
window->line_cnt = 0;
}
}
else
{
scroll_window(window);
if (window->cursor ==
(window->display_size - 1))
window->hold_on_next_rite = 1;
}
}
else if (window->visible)
{
term_cr();
term_newline();
}
if (window->visible)
{
high = display_highlight(OFF);
term_flush();
}
}
if (window->visible)
set_current_screen(old_current_screen);
return (0);
}
/*
* cursor_not_in_display: This forces the cursor out of the display by
* setting the cursor window to null. This doesn't actually change the
* physical position of the cursor, but it will force rite() to reset the
* cursor upon its next call
*/
void
cursor_not_in_display()
{
Debug((3, "cursor_not_in_display: screen %d cursor_window set to NULL", current_screen->screennum));
current_screen->cursor_window = NULL;
}
/*
* cursor_in_display: this forces the cursor_window to be the
* current_screen->current_window.
* It is actually only used in hold.c to trick the system into thinking the
* cursor is in a window, thus letting the input updating routines move the
* cursor down to the input line. Dumb dumb dumb
*/
void
cursor_in_display()
{
Debug((3, "cursor_in_display: screen %d cursor_window set to window %d (%d)", current_screen->screennum, curr_scr_win->refnum, curr_scr_win->screen->screennum));
current_screen->cursor_window = curr_scr_win;
}
/*
* is_cursor_in_display: returns true if the cursor is in one of the windows
* (cursor_window is not null), false otherwise
*/
int
is_cursor_in_display()
{
if (current_screen->cursor_window)
return (1);
else
return (0);
}
void
redraw_resized(window, Info, AnchorTop)
Window *window;
ShrinkInfo Info;
int AnchorTop;
{
if (!AnchorTop)
{
if (Info.bottom < 0)
term_scroll(window->top+window->menu.lines+Info.bottom,
window->top + window->menu.lines +
window->display_size - 1,
Info.bottom);
else if (Info.bottom)
term_scroll(window->top+window->menu.lines,
window->top + window->menu.lines +
window->display_size -1, Info.bottom);
}
}
/*
* resize_display: After determining that the screen has changed sizes, this
* resizes all the internal stuff. If the screen grew, this will add extra
* empty display entries to the end of the display list. If the screen
* shrank, this will remove entries from the end of the display list. By
* doing this, we try to maintain as much of the display as possible.
*
* This has now been improved so that it returns enough information for
* redraw_resized to redisplay the contents of the window without having
* to redraw too much.
*/
ShrinkInfo
resize_display(window)
Window *window;
{
int cnt,
i;
Display *tmp, *pre_ip;
int Wrapped = 0;
ShrinkInfo Result;
Result.top = Result.bottom = 0;
Result.position = window->cursor;
if (dumb)
{
return Result;
}
if (!window->top_of_display)
{
window->top_of_display = (Display *)new_malloc(sizeof(Display));
window->top_of_display->line = NULL;
window->top_of_display->linetype = LT_UNLOGGED;
window->top_of_display->next = window->top_of_display;
window->display_ip = window->top_of_display;
window->old_size = 1;
}
/* cnt = size - window->display_size; */
cnt = window->display_size - window->old_size;
if (cnt > 0)
{
Display *new = NULL;
/*
* screen got bigger: got to last display entry and link in new
* entries
*/
for (tmp = window->top_of_display, i = 0;
i < (window->old_size - 1);
i++, tmp = tmp->next);
for (i = 0; i < cnt; i++)
{
new = (Display *) new_malloc(sizeof(Display));
new->line = NULL;
new->linetype = LT_UNLOGGED;
new->next = tmp->next;
tmp->next = new;
}
if (window->display_ip == window->top_of_display &&
window->top_of_display->line)
window->display_ip = new;
Result.top = 0;
Result.bottom = cnt;
Result.position = 0;
}
else if (cnt < 0)
{
Display *ptr;
/*
* screen shrank: find last display entry we want to keep, and remove
* all after that point
*/
cnt = -cnt;
for (pre_ip = window->top_of_display;
pre_ip->next != window->display_ip;
pre_ip = pre_ip->next);
for (tmp = pre_ip->next, i =0; i < cnt; i++, tmp = ptr)
{
ptr = tmp->next;
if (tmp == window->top_of_display)
{
if (tmp->line)
Wrapped = 1;
window->top_of_display = ptr;
}
if (Wrapped)
Result.top--;
else
Result.bottom--;
new_free(&(tmp->line));
new_free(&tmp);
}
window->display_ip = pre_ip->next = tmp;
window->cursor += Result.top;
if (!window->scroll)
{
if (window->cursor == window->display_size)
window->cursor = 0;
new_free(&window->display_ip->line);
}
}
window->update |= REDRAW_DISPLAY_FULL | REDRAW_STATUS;
window->old_size = window->display_size;
return Result;
}
/*
* recalculate_windows: this is called when the terminal size changes (as
* when an xterm window size is changed). It recalculates the sized and
* positions of all the windows. Currently, all windows are rebalanced and
* window size proportionality is lost
*/
void
recalculate_windows()
{
int base_size,
size,
top,
extra;
Window *tmp;
if (dumb || current_screen->visible_windows == 0)
return;
base_size = ((current_screen->li - 1) / current_screen->visible_windows) - 1;
extra = (current_screen->li - 1) - ((base_size + 1)*current_screen->visible_windows);
top = 0;
for (tmp = current_screen->window_list; tmp; tmp = tmp->next)
{
tmp->update |= REDRAW_DISPLAY_FULL | REDRAW_STATUS;
if (extra)
{
extra--;
size = base_size + 1;
}
else
size = base_size;
#ifdef SCROLL_AFTER_DISPLAY
tmp->display_size = size - tmp->menu.lines
- tmp->double_status - 1;
#else
tmp->display_size = size - tmp->menu.lines
- tmp->double_status;
#endif /* SCROLL_AFTER_DISPLAY */
if (tmp->display_size<=0)
tmp->display_size = 1;
tmp->top = top;
tmp->bottom = top + size - tmp->double_status;
top += size + 1;
}
}
/*
* clear_window: This clears the display list for the given window, or
* current window if null is given.
*/
void
clear_window(window)
Window *window;
{
int i,
cnt;
if (dumb)
return;
if (window == NULL)
window = curr_scr_win;
erase_display(window);
term_move_cursor(0, window->top + window->menu.lines);
cnt = window->bottom - window->top - window->menu.lines;
for (i = 0; i < cnt; i++)
{
if (term_clear_to_eol())
term_space_erase(0);
term_newline();
}
term_flush();
}
/* clear_all_windows: This clears all *visible* windows */
void
clear_all_windows(unhold)
int unhold;
{
Window *tmp;
for (tmp = current_screen->window_list; tmp; tmp = tmp->next)
{
if (unhold)
hold_mode(tmp, OFF, 1);
clear_window(tmp);
}
}
/*
* redraw_window: This redraws the display list for the given window. Some
* special considerations are made if you are just redrawing one window as
* opposed to using this routine to redraw the whole screen one window at a
* time
*
* A negative value in just_one indicates not all of the window needs to
* be redrawn.
*/
static void
redraw_window(window, just_one, backscroll)
Window *window;
int just_one;
int backscroll;
{
Display *tmp;
int i;
int StartPoint;
int yScr;
if (dumb || !window->visible)
return;
window = window ? window : curr_scr_win;
if (just_one < 0)
{
/* This part of the window is scrolling into view */
StartPoint = -just_one;
just_one = 0;
}
else
{
StartPoint = 0;
if (window->scrolled_lines)
display_lastlog_lines(window->scrolled_lines - window->display_size,
window->scrolled_lines, window);
}
if (window->menu.menu)
ShowMenuByWindow(window, just_one ? SMF_ERASE : 0);
if (window->scrolled_lines + StartPoint < window->display_size)
yScr = window->scrolled_lines + StartPoint;
else
yScr = 0;
term_move_cursor(0, window->top+window->menu.lines+yScr);
/*
* if (term_clear_to_eol())
* { term_space_erase(0); term_cr(); }
*/
for (tmp = window->top_of_display, i = 0; i < window->display_size-window->scrolled_lines; i++, tmp = tmp->next)
{
if (i < StartPoint)
continue;
if (tmp->line)
rite(window, tmp->line, 1, 1, backscroll, 0);
else
{
if (just_one)
{
if (term_clear_to_eol())
term_space_erase(0);
}
term_newline();
}
}
term_flush();
}
/*
* recalculate_window_positions: This runs through the window list and
* re-adjusts the top and bottom fields of the windows according to their
* current positions in the window list. This doesn't change any sizes of
* the windows
*/
void
recalculate_window_positions()
{
Window *tmp;
int top;
top = 0;
for (tmp = current_screen->window_list; tmp; tmp = tmp->next)
{
tmp->update |= REDRAW_DISPLAY_FULL | REDRAW_STATUS;
tmp->top = top;
tmp->bottom = top + tmp->display_size + tmp->menu.lines;
top += tmp->display_size + tmp->menu.lines + 1 +
tmp->double_status;
}
}
/*
* redraw_all_windows: This basically clears and redraws the entire display
* portion of the screen. All windows and status lines are draws. This does
* nothing for the input line of the screen. Only visible windows are drawn
*/
void
redraw_all_windows()
{
Window *tmp;
if (dumb)
return;
for (tmp = current_screen->window_list; tmp; tmp = tmp->next)
tmp->update |= REDRAW_STATUS | REDRAW_DISPLAY_FAST;
}
static void
decode_colour(ptr, fg, bg)
u_char **ptr;
int *fg;
int *bg;
{
char c;
/* At input, *ptr points to the first char after ^C */
*fg = *bg = 16; /* both unset */
/*
* mIRC doesn't accept codes beginning with comma (,),
* VIRC accepts. mIRC is out authority in this case.
*/
c = **ptr;
if (c < '0' || c > '9')
{
--*ptr;
return;
}
*fg = c - '0';
c = (*ptr)[1];
if (c >= '0' && c <= '9')
{
++*ptr;
*fg = *fg * 10 + c - '0';
c = (*ptr)[1];
}
if (c == ',')
{
/*
* mIRC doesn't eat the comma if it is not followed by
* a number, VIRC does. mIRC is again the authority.
*/
c = (*ptr)[2];
if (c >= '0' && c <= '9')
{
*bg = c - '0';
*ptr += 2;
c = (*ptr)[1];
if (c >= '0' && c <= '9')
{
++*ptr;
*bg = *bg * 10 + c - '0';
}
}
}
/* At this point, *ptr points to last char WITHIN the colour code */
}
/* strlen() with internal format codes stripped */
/* Return value: number of columns this string takes */
int
my_strlen_i(c)
u_char *c;
{
int result = 0;
struct mb_data mbdata;
#ifdef HAVE_ICONV_OPEN
mbdata_init(&mbdata, CP(irc_encoding));
#else
mbdata_init(&mbdata, NULL);
#endif /* HAVE_ICONV_OPEN */
while (*c)
{
if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
{
/* These don't add visual length */
++c;
continue;
}
if (*c == COLOUR_TAG)
{
c += 3;
/* Colour code didn't add visual length */
continue;
}
/* Anything else is character data. */
decode_mb(c, NULL, &mbdata);
result += mbdata.num_columns;
c += mbdata.input_bytes;
}
mbdata_done(&mbdata);
return result;
}
/* strlen() with colour codes stripped */
/* Return value: Number of columns this string takes */
int
my_strlen_c(c)
u_char *c;
{
int result = 0;
struct mb_data mbdata;
#ifdef HAVE_ICONV_OPEN
mbdata_init(&mbdata, CP(irc_encoding));
#else
mbdata_init(&mbdata, NULL);
#endif /* HAVE_ICONV_OPEN */
while (*c)
{
if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
{
/* These don't add visual length */
++c;
continue;
}
if (*c == COLOUR_TAG)
{
int fg, bg;
++c;
decode_colour(&c, &fg, &bg);
++c;
/* Colour code didn't add visual length */
continue;
}
/* Anything else is character data. */
decode_mb(c, NULL, &mbdata);
result += mbdata.num_columns;
c += mbdata.input_bytes;
}
mbdata_done(&mbdata);
return result;
}
/* strlen() with colour codes converted to internal codes */
/* Doesn't do actual conversion */
/* Return value: Number of bytes this string takes when converted */
int
my_strlen_ci(c)
u_char *c;
{
int result = 0;
struct mb_data mbdata;
#ifdef HAVE_ICONV_OPEN
mbdata_init(&mbdata, CP(irc_encoding));
#else
mbdata_init(&mbdata, NULL);
#endif /* HAVE_ICONV_OPEN */
while (*c)
{
if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
{
/* These are preserved */
++c;
++result;
continue;
}
if (*c == COLOUR_TAG)
{
int fg, bg;
++c;
decode_colour(&c, &fg, &bg);
++c;
/* Modifies into three characters */
result += 3;
continue;
}
/* Anything else is character data. */
decode_mb(c, NULL, &mbdata);
result += mbdata.output_bytes;
c += mbdata.input_bytes;
}
mbdata_done(&mbdata);
return result;
}
/* strcpy() with colour codes converted to internal codes */
/* Converts the codes */
void
my_strcpy_ci(dest, c)
u_char *dest;
u_char *c;
{
struct mb_data mbdata;
#ifdef HAVE_ICONV_OPEN
mbdata_init(&mbdata, CP(irc_encoding));
#else
mbdata_init(&mbdata, NULL);
#endif /* HAVE_ICONV_OPEN */
/* FIXME: No array bound checking - very insecure */
while (*c)
{
if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
{
*dest++ = *c++;
continue;
}
if (*c == COLOUR_TAG)
{
int fg, bg;
*dest++ = *c++;
decode_colour(&c, &fg, &bg);
*dest++ = fg+1;
*dest++ = bg+1;
++c;
continue;
}
/* Anything else is character data. */
decode_mb(c, dest, &mbdata);
dest += mbdata.output_bytes;
c += mbdata.input_bytes;
}
mbdata_done(&mbdata);
*dest++ = '\0';
}
#define MAXIMUM_SPLITS 40
static u_char **
split_up_line(str)
u_char *str;
{
static u_char *output[MAXIMUM_SPLITS] =
{
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL
};
u_char lbuf[BIG_BUFFER_SIZE];
u_char *ptr;
u_char *cont_ptr,
*cont = NULL,
*temp = NULL,
*mystr = NULL,
c;
int pos = 0,
col = 0,
nd_cnt = 0,
word_break = 0,
start = 0,
i,
indent = 0,
beep_cnt = 0,
beep_max,
tab_cnt = 0,
tab_max,
line = 0;
int bold_state = 0,
invert_state = 0,
underline_state = 0,
fg_colour_state = 16,
bg_colour_state = 16;
size_t len;
struct mb_data mbdata;
#ifdef HAVE_ICONV_OPEN
mbdata_init(&mbdata, CP(irc_encoding));
#else
mbdata_init(&mbdata, NULL);
#endif /* HAVE_ICONV_OPEN */
bzero(lbuf, sizeof(lbuf));
for (i = 0; i < MAXIMUM_SPLITS; i++)
new_free(&output[i]);
if (!str || !*str)
{
/* special case to make blank lines show up */
malloc_strcpy(&mystr, UP(" "));
str = mystr;
}
beep_max = get_int_var(BEEP_VAR) ? get_int_var(BEEP_MAX_VAR) : -1;
tab_max = get_int_var(TAB_VAR) ? get_int_var(TAB_MAX_VAR) : -1;
for (ptr = (u_char *) str; *ptr && (pos < sizeof(lbuf) - 8); ptr++)
{
switch (*ptr)
{
case '\007': /* bell */
if (beep_max == -1)
{
lbuf[pos++] = REV_TOG;
lbuf[pos++] = (*ptr & 127) | 64;
lbuf[pos++] = REV_TOG;
nd_cnt += 2;
col++;
}
else if (!beep_max || (++beep_cnt <= beep_max))
{
lbuf[pos++] = *ptr;
nd_cnt++;
col++;
}
break;
case '\011': /* tab */
if (tab_max && (++tab_cnt > tab_max))
{
lbuf[pos++] = REV_TOG;
lbuf[pos++] = (*ptr & 127) | 64;
lbuf[pos++] = REV_TOG;
nd_cnt += 2;
col++;
}
else
{
if (indent == 0)
indent = -1;
len = 8 - (col % 8);
word_break = pos;
for (i = 0; i < len; i++)
lbuf[pos++] = ' ';
col += len;
}
break;
case UND_TOG:
case ALL_OFF:
case REV_TOG:
case BOLD_TOG:
switch(*ptr)
{
case UND_TOG:
underline_state = !underline_state;
break;
case REV_TOG:
invert_state = !invert_state;
break;
case BOLD_TOG:
bold_state = !bold_state;
break;
case ALL_OFF:
underline_state = invert_state = bold_state = 0;
fg_colour_state = bg_colour_state = 16;
break;
}
lbuf[pos++] = *ptr;
nd_cnt++;
break;
case COLOUR_TAG:
{
int fg, bg;
lbuf[pos++] = *ptr++;
decode_colour(&ptr, &fg, &bg);
lbuf[pos++] = (u_char)(fg_colour_state = fg) + 1;
lbuf[pos++] = (u_char)(bg_colour_state = bg) + 1;
nd_cnt += 3;
break;
}
case ' ': /* word break */
if (indent == 0)
indent = -1;
word_break = pos;
lbuf[pos++] = *ptr;
col++;
break;
default: /* Anything else, make it displayable */
{
if (indent == -1)
indent = pos - nd_cnt;
/* FIXME: No array bound checking - very insecure */
decode_mb(ptr, lbuf+pos, &mbdata);
/* If the sequence takes multiple columns,
* be wrappable now when we can
*/
if (mbdata.num_columns > 1)
word_break = pos;
pos += mbdata.output_bytes;
nd_cnt += mbdata.output_bytes - mbdata.num_columns;
ptr += mbdata.input_bytes - 1;
col += mbdata.num_columns;
break;
}
}
if (pos >= sizeof(lbuf)-1)
*ptr = '\0';
if (col >= current_screen->co)
{
char c1;
int oldpos;
/* one big long line, no word breaks */
if (word_break == 0)
word_break = pos - (col - current_screen->co);
c = lbuf[word_break];
c1 = lbuf[word_break+1];
lbuf[word_break] = FULL_OFF;
lbuf[word_break+1] = '\0';
if (cont)
{
malloc_strcpy(&temp, cont);
malloc_strcat(&temp, &(lbuf[start]));
}
else
malloc_strcpy(&temp, &(lbuf[start]));
malloc_strcpy(&output[line++], temp);
lbuf[word_break] = c;
lbuf[word_break+1] = c1;
start = word_break;
word_break = 0;
while (lbuf[start] == ' ')
start++;
if (start > pos)
start = pos;
if (!(cont_ptr = get_string_var(CONTINUED_LINE_VAR)))
cont_ptr = empty_string;
if (get_int_var(INDENT_VAR) && (indent < current_screen->co / 3))
{
if (!cont) /* Build it only the first time */
{
/* Visual length */
int vlen = my_strlen_c(cont_ptr);
/* Converted length */
int clen = my_strlen_ci(cont_ptr);
int padlen = indent - vlen;
if (padlen < 0)
padlen = 0;
cont = (u_char *) new_malloc(clen + padlen + 1);
my_strcpy_ci(cont, cont_ptr);
for (; padlen > 0; --padlen)
{
cont[clen++] = ' ';
/* Add space */
}
cont[clen] = '\0';
}
}
else
{
/* Converted length */
int clen = my_strlen_ci(cont_ptr);
/* No indent, just prefix only */
cont = (u_char *) new_malloc(clen + 1);
my_strcpy_ci(cont, cont_ptr);
}
/* cont contains internal codes, so use my_strlen_i */
col = my_strlen_i(cont) + (pos - start);
/* rebuild previous state */
oldpos = pos;
if (underline_state)
{
lbuf[pos++] = UND_TOG;
nd_cnt++;
}
if (invert_state)
{
lbuf[pos++] = REV_TOG;
nd_cnt++;
}
if (bold_state)
{
lbuf[pos++] = BOLD_TOG;
nd_cnt++;
}
if (fg_colour_state != 16 || bg_colour_state != 16)
{
lbuf[pos++] = COLOUR_TAG;
lbuf[pos++] = fg_colour_state + 1;
lbuf[pos++] = bg_colour_state + 1;
nd_cnt++;
}
if (pos != oldpos)
while(start < oldpos)
lbuf[pos++] = lbuf[start++];
}
}
mbdata_done(&mbdata);
lbuf[pos++] = FULL_OFF;
lbuf[pos] = '\0';
if (lbuf[start])
{
if (cont)
{
malloc_strcpy(&temp, cont);
malloc_strcat(&temp, &(lbuf[start]));
}
else
malloc_strcpy(&temp, &(lbuf[start]));
malloc_strcpy(&output[line++], temp);
}
if (mystr)
new_free(&mystr);
new_free(&cont);
new_free(&temp);
return output;
}
/*
* add_to_window: adds the given string to the display. No kidding. This
* routine handles the whole ball of wax. It keeps track of what's on the
* screen, does the scrolling, everything... well, not quite everything...
* The CONTINUED_LINE idea thanks to Jan L. Peterson (jlp@hamblin.byu.edu)
*
* At least it used to. Now most of this is done by split_up_line, and this
* function just dumps it onto the screen. This is because the scrollback
* functions need to be able to figure out how to split things up too.
*/
static void
add_to_window(window, str)
Window *window;
u_char *str;
{
int flag;
flag = do_hook(WINDOW_LIST, "%u %s", window->refnum, str);
if (flag)
{
size_t len = my_strlen(str);
u_char *my_str = new_malloc(len + 2);
u_char **lines;
int logged;
add_to_log(window->log_fp, str);
add_to_lastlog(window, str);
display_highlight(OFF);
display_bold(OFF);
display_colours(get_int_var(FOREGROUND_COLOUR_VAR),
get_int_var(BACKGROUND_COLOUR_VAR));
bcopy(str, my_str, len);
my_str[len] = ALL_OFF;
my_str[len + 1] = '\0';
logged = islogged(window);
/* For each of the lines created by split_up_line(),
* display the line.
* Rite() will assume each input line fits on 1 line.
*/
for (lines = split_up_line(my_str); *lines; lines++)
{
rite(window, *lines, 0, 0, 0, logged);
if (logged == 1)
logged = 2;
}
new_free(&my_str);
term_flush();
}
}
/*
* XXX this is a temporary internal interface that will go
* away in the near future.
*/
int in_redirect;
/*
* add_to_screen: This adds the given null terminated buffer to the screen.
* That is, it determines which window the information should go to, which
* lastlog the information should be added to, which log the information
* should be sent to, etc
*/
void
add_to_screen(incoming)
u_char *incoming;
{
int flag;
Window *tmp;
u_char buffer[BIG_BUFFER_SIZE];
/* eek! */
if (!current_screen)
{
puts(CP(incoming));
fflush(stdout);
return;
}
/* Handles output redirection first */
if (!in_redirect && current_screen->redirect_name &&
from_server == current_screen->redirect_server)
{
int i;
/*
* More general stuff by Bisqwit, allow
* multiple target redirection (with different targets)
*/
in_redirect = 1;
i = in_on_who;
in_on_who = 0; /* To allow redirecting /who, /whois and /join */
redirect_msg(current_screen->redirect_name, incoming);
in_on_who = i;
in_redirect = 0;
}
if (dumb)
{
/* FIXME: Do iconv for "incoming" in dumb mode too */
add_to_lastlog(curr_scr_win, incoming);
if (do_hook(WINDOW_LIST, "%u %s", curr_scr_win->refnum, incoming))
puts(CP(incoming));
term_flush();
return;
}
if (in_window_command)
update_all_windows();
if ((who_level == LOG_CURRENT) && (curr_scr_win->server == from_server))
{
add_to_window(curr_scr_win, incoming);
return;
}
if (to_window)
{
add_to_window(to_window, incoming);
return;
}
/*
* XXX - hack alert
* If /echo or /xecho set the message_from, use it here before we
* look at who_from. If not, look at it afterwards.
*/
if (who_level && my_echo_set_message_from)
{
flag = 1;
while ((tmp = traverse_all_windows(&flag)) != NULL)
{
if (((from_server == tmp->server) || (from_server == -1)) &&
(who_level & tmp->window_level))
{
add_to_window(tmp, incoming);
return;
}
}
}
if (who_from)
{
if (is_channel(who_from))
{
ChannelList *chan;
if ((chan = lookup_channel(who_from, from_server, CHAN_NOUNLINK)))
{
add_to_window(chan->window ? chan->window : curr_scr_win, incoming);
return;
}
if (who_level == LOG_DCC)
{
my_strcpy(buffer, "=");
my_strmcat(buffer, who_from, sizeof buffer);
if ((chan = lookup_channel(buffer, from_server, CHAN_NOUNLINK)))
{
add_to_window(chan->window ? chan->window : curr_scr_win, incoming);
return;
}
}
}
else
{
flag = 1;
while ((tmp = traverse_all_windows(&flag)) != NULL)
{
if (tmp->query_nick &&
(((who_level == LOG_MSG || who_level == LOG_NOTICE)
&& !my_stricmp(who_from, tmp->query_nick) &&
from_server == tmp->server) ||
(who_level == LOG_DCC &&
(*tmp->query_nick == '=' || *tmp->query_nick == '@') &&
my_stricmp(who_from, tmp->query_nick + 1) == 0)))
{
add_to_window(tmp, incoming);
return;
}
}
flag = 1;
while ((tmp = traverse_all_windows(&flag)) != NULL)
{
if (from_server == tmp->server)
{
if (find_in_list((List **)(void *)&(tmp->nicks),
who_from, !USE_WILDCARDS))
{
add_to_window(tmp, incoming);
return;
}
}
}
}
}
if (who_level && !my_echo_set_message_from)
{
flag = 1;
while ((tmp = traverse_all_windows(&flag)) != NULL)
{
if (((from_server == tmp->server) || (from_server == -1)) &&
(who_level & tmp->window_level))
{
add_to_window(tmp, incoming);
return;
}
}
}
if (from_server == curr_scr_win->server)
tmp = curr_scr_win;
else
{
flag = 1;
while ((tmp = traverse_all_windows(&flag)) != NULL)
{
if (tmp->server == from_server)
break;
}
if (!tmp)
tmp = curr_scr_win;
}
add_to_window(tmp, incoming);
}
/*
* update_all_windows: This goes through each visible window and draws the
* necessary portions according the the update field of the window.
*/
void
update_all_windows()
{
Window *tmp;
int fast_window,
full_window,
r_status,
u_status;
for (tmp = current_screen->window_list; tmp; tmp = tmp->next)
{
if (tmp->display_size != tmp->old_size)
resize_display(tmp);
if (tmp->update)
{
fast_window = tmp->update & REDRAW_DISPLAY_FAST;
full_window = tmp->update & REDRAW_DISPLAY_FULL;
r_status = tmp->update & REDRAW_STATUS;
u_status = tmp->update & UPDATE_STATUS;
if (full_window)
redraw_window(tmp, 1, 0);
else if (fast_window)
redraw_window(tmp, 0, 0);
if (r_status)
update_window_status(tmp, 1);
else if (u_status)
update_window_status(tmp, 0);
}
tmp->update = 0;
}
for (tmp = invisible_list; tmp; tmp = tmp->next)
{
if (tmp->display_size != tmp->old_size)
resize_display(tmp);
tmp->update = 0;
}
update_input(UPDATE_JUST_CURSOR);
term_flush();
}
/*
* create_refnum: this generates a reference number for a new window that is
* not currently is use by another window. A refnum of 0 is reserved (and
* never returned by this routine). Using a refnum of 0 in the message_to()
* routine means no particular window (stuff goes to CRAP)
*/
static u_int
create_refnum()
{
unsigned int new_refnum = 1;
Window *tmp;
int done = 0,
flag;
while (!done)
{
done = 1;
if (new_refnum == 0)
new_refnum++;
flag = 1;
while ((tmp = traverse_all_windows(&flag)) != NULL)
{
if (tmp->refnum == new_refnum)
{
done = 0;
new_refnum++;
break;
}
}
}
return (new_refnum);
}
/*
* new_window: This creates a new window on the screen. It does so by either
* splitting the current window, or if it can't do that, it splits the
* largest window. The new window is added to the window list and made the
* current window
*/
Window *
new_window()
{
Window *new;
static int no_screens = 1;
if (no_screens)
{
set_current_screen(create_new_screen());
main_screen = current_screen;
no_screens = 0;
}
if (dumb && (current_screen->visible_windows == 1))
return NULL;
new = (Window *) new_malloc(sizeof(Window));
new->refnum = create_refnum();
if (curr_scr_win)
new->server = curr_scr_win->server;
else
new->server = primary_server;
new->prev_server = -1;
new->line_cnt = 0;
if (current_screen->visible_windows == 0)
new->window_level = LOG_DEFAULT;
else
new->window_level = LOG_NONE;
new->hold_mode = get_int_var(HOLD_MODE_VAR);
new->scroll = get_int_var(SCROLL_VAR);
new->lastlog_head = 0;
new->lastlog_tail = 0;
new->nicks = 0;
new->lastlog_level = real_lastlog_level();
new->name = 0;
new->prompt = 0;
new->lastlog_size = 0;
new->held = OFF;
new->last_held = OFF;
new->current_channel = 0;
new->bound_channel = 0;
new->query_nick = 0;
new->hold_on_next_rite = 0;
new->status_line[0] = NULL;
new->status_line[1] = NULL;
new->double_status = 0;
new->top_of_display = 0;
new->display_ip = 0;
new->display_size = 1;
new->old_size = 1;
new->hold_head = 0;
new->hold_tail = 0;
new->held_lines = 0;
new->scrolled_lines = 0;
new->new_scrolled_lines = 0;
new->next = 0;
new->prev = 0;
new->cursor = 0;
new->visible = 1;
new->screen = current_screen;
new->logfile = 0;
new->log = 0;
new->log_fp = 0;
new->miscflags = 0;
new->update = 0;
new->menu.lines = 0;
new->menu.menu = 0;
new->notify_level = real_notify_level();
new->server_group = 0;
new->sticky = 1;
resize_display(new);
if (add_to_window_list(new))
set_current_window(new);
term_flush();
return (new);
}
void
close_all_screen()
{
Screen *screen;
for (screen = screen_list; screen && screen != current_screen;
screen = screen->next)
if (screen->alive && screen->fdin != 0)
new_close(screen->fdin);
}
#ifdef WINDOW_CREATE
Window *
create_additional_screen(wanted_type)
int wanted_type;
{
Window *win;
Screen *oldscreen;
u_char *displayvar,
*termvar;
int screen_type = ST_NOTHING;
struct sockaddr_un sock, error_sock,
*sockaddr = &sock,
*error_sockaddr = &error_sock,
NewSock;
socklen_t NsZ;
int s, es;
fd_set fd_read;
struct timeval time_out;
pid_t child;
int old_timeout;
#define IRCXTERM_MAX 10
u_char *ircxterm[IRCXTERM_MAX];
u_char *ircxterm_env;
u_char *xterm = (u_char *) 0;
u_char *def_xterm = get_string_var(XTERM_PATH_VAR);
u_char *p, *q;
u_char buffer[BIG_BUFFER_SIZE];
pid_t pid = getpid();
int ircxterm_num;
int i;
static int cycle = 0;
int mycycle = cycle++;
#ifdef DAEMON_UID
if (DAEMON_UID == getuid())
{
say("you are not permitted to use WINDOW CREATE");
return (Window *) 0;
}
#endif /* DAEMON_UID */
if (!def_xterm)
def_xterm = UP("xterm");
ircxterm_num = 0;
p = ircxterm_env = my_getenv("IRCXTERM");
if (p)
{
q = ircxterm_env + my_strlen(ircxterm_env);
while (p < q && ircxterm_num < IRCXTERM_MAX)
{
while (':' == *p)
p++;
if ('\0' == *p)
break;
ircxterm[ircxterm_num++] = p;
while (':' != *p && '\0' != *p)
p++;
if (':' == *p)
{
*p = '\0';
p++;
}
}
}
else
{
ircxterm[ircxterm_num] = def_xterm;
ircxterm_num++;
}
/*
* Environment variable STY has to be set for screen to work.. so it is
* the best way to check screen.. regardless of what TERM is, the
* execpl() for screen won't just open a new window if STY isn't set,
* it will open a new screen process, and run the wserv in its first
* window, not what we want... -phone
*/
if ((wanted_type == ST_NOTHING || wanted_type == ST_SCREEN) &&
0 != my_getenv("STY"))
screen_type = ST_SCREEN;
else if ((wanted_type == ST_NOTHING || wanted_type == ST_XTERM) &&
(u_char *) 0 != (displayvar = my_getenv("DISPLAY")) &&
(u_char *) 0 != (termvar = my_getenv("TERM")))
{
screen_type = ST_XTERM;
if (0 == my_strncmp(termvar, "sun", 3))
{
xterm = def_xterm;
}
else
{
for (; *termvar; termvar++)
{
for (i = 0; i < ircxterm_num; i++)
{
if (!my_strncmp(termvar, ircxterm[i], my_strlen(ircxterm[i])))
{
xterm = ircxterm[i];
termvar = empty_string;
break;
}
}
}
}
if (!xterm)
xterm = def_xterm;
}
if (screen_type == ST_NOTHING)
{
say("I don't know how to create new windows for this terminal");
return (Window *) 0;
}
say("Opening new %s...",
screen_type == ST_XTERM ? "window" :
screen_type == ST_SCREEN ? "screen" :
"wound" );
snprintf(sock.sun_path, sizeof sock.sun_path, "/tmp/irc_%08d_%x", (int) pid, mycycle);
sock.sun_family = AF_UNIX;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
say("Can't create UNIX socket, punting /WINDOW CREATE");
return (Window *) 0;
}
if (bind(s, (struct sockaddr *) &sock, (int)(2 + my_strlen(sock.sun_path))) < 0)
{
say("Can't bind UNIX socket, punting /WINDOW CREATE");
return (Window *) 0;
}
if (listen(s, 1) < 0)
{
say("Can't bind UNIX socket, punting /WINDOW CREATE");
return (Window *) 0;
}
snprintf(error_sock.sun_path, sizeof error_sock.sun_path, "/tmp/irc_error_%08d_%x", (int) pid, mycycle);
error_sock.sun_family = AF_UNIX;
if ((es = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
say("Can't create UNIX socket, punting /WINDOW CREATE");
return (Window *) 0;
}
if (bind(es, (struct sockaddr *) &error_sock, (int)(2 + my_strlen(error_sock.sun_path))) < 0)
{
say("Can't bind UNIX socket, punting /WINDOW CREATE");
return (Window *) 0;
}
if (listen(es, 1) < 0)
{
say("Can't bind UNIX socket, punting /WINDOW CREATE");
return (Window *) 0;
}
oldscreen = current_screen;
set_current_screen(create_new_screen());
if (0 == (child = fork()))
{
setuid(getuid());
setgid(getgid());
/*
* Unlike most other cases, it is important here to close down
* *ALL* unneeded file descriptors. Failure to do so can cause
* Things like server and DCC connections to fail to close on
* request. This isn't a problem with "screen", but is with X.
*/
new_close(s);
new_close(es);
close_all_screen();
close_all_dcc();
close_all_exec();
close_all_server();
i = 0;
if (screen_type == ST_SCREEN)
{
char *args[64];
u_char *ss,
*t,
*opts = NULL;
Debug((3, "going to execvp screen wserv..."));
args[i++] = "screen";
if ((ss = get_string_var(SCREEN_OPTIONS_VAR)) != NULL)
{
malloc_strcpy(&opts, ss);
while ((t = next_arg(opts, &opts)) != NULL)
{
Debug((3, "added option `%s'", t));
args[i++] = CP(t);
}
}
args[i++] = WSERV_PATH;
args[i++] = sockaddr->sun_path;
args[i++] = error_sockaddr->sun_path;
Debug((3, "added: %s %s %s", args[i-3], args[i-2], args[i-1]));
args[i++] = NULL;
execvp("screen", args);
}
else if (screen_type == ST_XTERM)
{
int lines,
columns;
char *args[64];
u_char geom[20],
*ss,
*t,
*opts = NULL;
Debug((3, "going to execvp xterm wserv..."));
copy_window_size(&lines, &columns);
snprintf(CP(geom), sizeof geom, "%dx%d", columns, lines);
args[i++] = CP(xterm);
Debug((3, "xterm name is %s", args[0]));
if ((ss = get_string_var(XTERM_GEOMOPTSTR_VAR)) != NULL)
args[i++] = CP(ss);
else
args[i++] = "-geom";
args[i++] = CP(geom);
Debug((3, "added geom: %s %s", args[i-2], args[i-1]));
if ((ss = get_string_var(XTERM_OPTIONS_VAR)) != NULL)
{
malloc_strcpy(&opts, ss);
Debug((3, "got xterm options: %s", opts));
while ((t = next_arg(opts, &opts)) != NULL)
{
Debug((3, "added option `%s'", t));
args[i++] = CP(t);
}
}
args[i++] = "-e";
args[i++] = WSERV_PATH;
args[i++] = sockaddr->sun_path;
args[i++] = error_sockaddr->sun_path;
Debug((3, "added: %s %s %s %s", args[i-4], args[i-3], args[i-2], args[i-1]));
args[i] = NULL;
execvp(CP(xterm), args);
}
perror("execve");
unlink(sockaddr->sun_path);
unlink(error_sockaddr->sun_path);
exit(errno ? errno : -1);
}
NsZ = sizeof(NewSock);
FD_ZERO(&fd_read);
FD_SET(s, &fd_read);
FD_SET(es, &fd_read);
time_out.tv_sec = (time_t) 5;
time_out.tv_usec = 0;
sleep(1);
/* using say(), yell() can be bad in this next section of code. */
switch (select(NFDBITS , &fd_read, NULL, NULL, &time_out))
{
case -1:
case 0:
errno = get_child_exit(child);
new_close(s);
new_close(es);
kill_screen(current_screen);
kill(child, SIGKILL);
last_input_screen = oldscreen;
set_current_screen(oldscreen);
yell("child %s with %d", (errno < 1) ? "signaled" : "exited",
(errno < 1) ? -errno : errno);
win = (Window *) 0;
break;
default:
current_screen->fdin = current_screen->fdout =
accept(s, (struct sockaddr *) &NewSock, &NsZ);
if (current_screen->fdin < 0)
{
win = (Window *) 0;
break;
}
current_screen->wservin = accept(es, (struct sockaddr *) &NewSock,
&NsZ);
if (current_screen->wservin < 0)
{
win = (Window *) 0;
break;
}
current_screen->fpin = current_screen->fpout =
fdopen(current_screen->fdin, "r+");
term_set_fp(current_screen->fpout);
new_close(s);
new_close(es);
old_timeout = dgets_timeout(5);
/*
* dgets returns 0 on EOF and -1 on timeout. both of these are
* error conditions in this case, so we bail out here.
*/
if (dgets(buffer, sizeof buffer, current_screen->fdin, (u_char *) 0) < 1)
{
new_close(current_screen->fdin);
kill_screen(current_screen);
kill(child, SIGKILL);
last_input_screen = oldscreen;
set_current_screen(oldscreen);
(void) dgets_timeout(old_timeout);
win = (Window *) 0;
break;
}
else
malloc_strcpy(¤t_screen->tty_name, buffer);
win = new_window();
(void) refresh_screen(0, NULL);
set_current_screen(oldscreen);
(void) dgets_timeout(old_timeout);
}
unlink(sockaddr->sun_path);
unlink(error_sockaddr->sun_path);
return win;
}
#endif /* WINDOW_CREATE */
/*
* below here is magic. this is where the real work of scrolling
* backwards and forwards through the history is done. basically,
* what happens is some functions are called from other code to
* make scroll requests: forward, back, start, end. these functions
* do some minor house keeping, and then call the function
* display_lastlog_lines() to do the real work. note that these
* functions call display_lastlog_lines() with lastlog line numbers,
* which is possibly (probably) not the same as the number of lines
* "Lastlog" lines in the window's history. display_lastlog_lines()
* uses repeated calls to the function next_line_back() which does
* the work for splitting each "Lastlog" entry up into lines to be
* displayed on the screen. when the desired position is found,
* these lines are written to the screen.
*/
/*
* call initially with a non-NULL "window" argument to initialise
* the count. then, each further call to next_line_back() will
* return the next line of the screen output, going backwards
* through the lastlog history. use use split_up_line() from earlier
* in this file to do the actual splitting of each individiual line.
*/
static u_char *
next_line_back(window)
Window *window;
{
static int row;
static Lastlog *LogLine;
u_char **TheirLines;
static u_char *ScreenLines[MAXIMUM_SPLITS] =
{
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL
};
if (window)
{
LogLine = window->lastlog_head;
row = -1;
}
if (row <= 0)
{
for (row = 0; ScreenLines[row]; row++)
new_free(&ScreenLines[row]);
if (!window && LogLine)
LogLine = LogLine->next;
if (!LogLine)
return NULL;
TheirLines = split_up_line(LogLine->msg);
for (row = 0; TheirLines[row]; row++)
{
ScreenLines[row] = TheirLines[row];
TheirLines[row] = NULL;
}
if (window)
return NULL;
}
return ScreenLines[--row];
}
/*
* how many lastlog lines in this window?
*/
static int
lastlog_lines(window)
Window *window;
{
Display *Disp;
int num_lines = 0, i;
(void)next_line_back(window);
for (i = window->new_scrolled_lines; i--; num_lines++)
(void)next_line_back(NULL);
for (i = 0, Disp = window->top_of_display; i < window->display_size;
Disp = Disp->next, i++)
if (Disp->linetype)
(void)next_line_back(NULL);
while (next_line_back(NULL))
num_lines++;
Debug((3, " num_lines ends up as %d", num_lines));
return (num_lines);
}
/*
* in window "window", display the lastlog lines from "start" to "end".
*/
static void
display_lastlog_lines(start, end, window)
int start,
end;
Window *window;
{
Display *Disp;
u_char *Line;
int i;
(void)next_line_back(window);
for (i = window->new_scrolled_lines; i--;)
(void)next_line_back(NULL);
for (i = 0, Disp = window->top_of_display; i < window->display_size;
Disp = Disp->next, i++)
if (Disp->linetype)
(void)next_line_back(NULL);
for (i = 0; i < start; i++)
(void)next_line_back(NULL);
for (; i < end; i++)
{
if (!(Line = next_line_back(NULL)))
break;
term_move_cursor(0, window->top + window->menu.lines +
window->scrolled_lines - i - 1);
rite(window, Line, 0, 0, 1, 0);
}
}
/*
* scrollback_{back,forw}wards_lines: scroll the named distance, called by
* internal functions here.
*/
static void
scrollback_backwards_lines(ScrollDist)
int ScrollDist;
{
Window *window;
int totallines;
Debug((3, "scrollback_backwards_lines(%d)", ScrollDist));
window = curr_scr_win;
if (!window->scrolled_lines && !window->scroll)
{
term_beep();
return;
}
totallines = lastlog_lines(window);
Debug((3, "totallines = %d, scrolled_lines = %d", totallines, window->scrolled_lines));
if (ScrollDist + window->scrolled_lines > totallines)
{
ScrollDist = totallines - window->scrolled_lines;
Debug((3, " adjusting ScrollDist to %d", ScrollDist));
}
if (ScrollDist == 0)
{
term_beep();
return;
}
window->scrolled_lines += ScrollDist;
Debug((3, "going to term_scroll(%d, %d, %d)",
window->top + window->menu.lines,
window->top + window->menu.lines + window->display_size - 1,
-ScrollDist));
term_scroll(window->top + window->menu.lines,
window->top + window->menu.lines + window->display_size - 1,
-ScrollDist);
Debug((3, "scrolled_lines=%d, new_scrolled_lines=%d, display_size=%d",
window->scrolled_lines,
window->new_scrolled_lines,
window->display_size));
Debug((3, "going to display_lastlog_lines(%d, %d, %s)",
window->scrolled_lines - ScrollDist,
window->scrolled_lines,
window->name));
display_lastlog_lines(window->scrolled_lines - ScrollDist,
window->scrolled_lines,
window);
cursor_not_in_display();
update_input(UPDATE_JUST_CURSOR);
window->update |= UPDATE_STATUS;
update_window_status(window, 0);
}
static void
scrollback_forwards_lines(ScrollDist)
int ScrollDist;
{
Window *window;
Debug((3, "scrollback_forward_lines(%d)", ScrollDist));
window = curr_scr_win;
if (!window->scrolled_lines)
{
term_beep();
return;
}
if (ScrollDist > window->scrolled_lines)
ScrollDist = window->scrolled_lines;
Debug((3, "scrolled_lines = %d", window->scrolled_lines));
window->scrolled_lines -= ScrollDist;
Debug((3, "going to term_scroll(%d, %d, %d)",
window->top + window->menu.lines,
window->top + window->menu.lines + window->display_size - 1,
ScrollDist));
term_scroll(window->top + window->menu.lines,
window->top + window->menu.lines + window->display_size - 1,
ScrollDist);
Debug((3, "scrolled_lines=%d, new_scrolled_lines=%d, display_size=%d",
window->scrolled_lines,
window->new_scrolled_lines,
window->display_size));
if (window->scrolled_lines < window->display_size)
redraw_window(window,
ScrollDist + window->scrolled_lines - window->display_size, 1);
Debug((3, "going to display_lastlog_lines(%d, %d, %s)",
window->scrolled_lines - window->display_size,
window->scrolled_lines - window->display_size + ScrollDist,
window->name));
display_lastlog_lines(window->scrolled_lines - window->display_size,
window->scrolled_lines - window->display_size + ScrollDist,
window);
cursor_not_in_display();
update_input(UPDATE_JUST_CURSOR);
if (!window->scrolled_lines)
{
window->new_scrolled_lines = 0;
if (window->hold_mode)
hold_mode(window, ON, 1);
else
hold_mode(window, OFF, 0);
}
window->update |= UPDATE_STATUS;
update_window_status(window, 0);
}
/*
* scrollback_{forw,back}wards: scrolls the window up or down half the screen.
* these are called when the respective key are pressed.
*/
void
scrollback_forwards(key, ptr)
u_int key;
u_char * ptr;
{
scrollback_forwards_lines(curr_scr_win->display_size/2);
}
void
scrollback_backwards(key, ptr)
u_int key;
u_char *ptr;
{
scrollback_backwards_lines(curr_scr_win->display_size/2);
}
/*
* scrollback_end: exits scrollback mode and moves the display back
* to the end of the scrollback buffer.
*/
void
scrollback_end(key, ptr)
u_int key;
u_char *ptr;
{
Window *window;
window = curr_scr_win;
window->new_scrolled_lines = 0;
if (!window->scrolled_lines)
{
term_beep();
return;
}
if (window->scrolled_lines < window->display_size)
scrollback_forwards_lines(window->scrolled_lines);
else
{
window->scrolled_lines = window->new_scrolled_lines = 0;
redraw_window(window, 1, 1);
cursor_not_in_display();
update_input(UPDATE_JUST_CURSOR);
if (window->hold_mode)
hold_mode(window, ON, 1);
else
hold_mode(window, OFF, 0);
window->update |= UPDATE_STATUS;
update_window_status(window, 0);
}
}
/*
* scrollback_start: moves the current screen back to the start of
* the scrollback buffer.
*/
void
scrollback_start(key, ptr)
u_int key;
u_char *ptr;
{
Window *window;
int num_lines;
window = curr_scr_win;
if (!window->lastlog_size)
{
term_beep();
return;
}
Debug((3, "scrollback_start: new_scrolled_lines=%d display_size=%d", window->new_scrolled_lines, window->display_size));
num_lines = lastlog_lines(window);
if (num_lines < window->display_size)
scrollback_backwards_lines(num_lines);
else
{
window->scrolled_lines = num_lines;
Debug((3, "going to display_lastlog_lines(%d, %d, %s)", num_lines - window->display_size, window->scrolled_lines, window->name));
display_lastlog_lines(num_lines - window->display_size,
window->scrolled_lines, window);
cursor_not_in_display();
update_input(UPDATE_JUST_CURSOR);
window->new_scrolled_lines = 0;
if (window->hold_mode)
hold_mode(window, ON, 1);
else
hold_mode(window, OFF, 0);
window->update |= UPDATE_STATUS;
update_window_status(window, 0);
}
}
void
screen_wserv_message(screen)
Screen *screen;
{
u_char buf[128], *rs, *cs, *comma;
int old_timeout, li, co;
old_timeout = dgets_timeout(0);
if (dgets(buf, 128, current_screen->wservin, (u_char *) 0) < 1)
{
/* this should be impossible. */
if (!is_main_screen(screen))
kill_screen(screen);
}
(void) dgets_timeout(old_timeout);
Debug((3, "screen_wserv_message: %s", buf));
/* should have "rows,cols\n" */
rs = buf;
comma = my_index(buf, ',');
if (comma == NULL)
{
yell("--- got wserv message: %s, no comma?", buf);
return;
}
*comma = 0;
cs = comma+1;
comma = my_index(buf, '\n');
if (comma != NULL)
*comma = 0;
li = atoi(CP(rs));
co = atoi(CP(cs));
Debug((3, "screen_wserv_message: li %d[%d] co %d[%d]", li, screen->li, co, screen->co));
if (screen->li != li || screen->co != co)
{
Screen *old_screen;
screen->li = li;
screen->co = co;
old_screen = current_screen;
current_screen = screen;
refresh_screen(0, 0);
current_screen = old_screen;
}
}
syntax highlighted by Code2HTML, v. 0.9.1