Implementation for console control events (includes process groups
support).

diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index d93ce86..fbea3e4 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -963,17 +963,21 @@
  */
 static HANDLER_DEF(int_handler)
 {
-    EXCEPTION_RECORD rec;
-    CONTEXT context;
+    extern int CONSOLE_HandleCtrlC(void);
+    if (!CONSOLE_HandleCtrlC())
+    {
+        EXCEPTION_RECORD rec;
+        CONTEXT context;
 
-    save_context( &context, HANDLER_CONTEXT );
-    rec.ExceptionCode    = CONTROL_C_EXIT;
-    rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
-    rec.ExceptionRecord  = NULL;
-    rec.ExceptionAddress = (LPVOID)context.Eip;
-    rec.NumberParameters = 0;
-    EXC_RtlRaiseException( &rec, &context );
-    restore_context( &context, HANDLER_CONTEXT );
+        save_context( &context, HANDLER_CONTEXT );
+        rec.ExceptionCode    = CONTROL_C_EXIT;
+        rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
+        rec.ExceptionRecord  = NULL;
+        rec.ExceptionAddress = (LPVOID)context.Eip;
+        rec.NumberParameters = 0;
+        EXC_RtlRaiseException( &rec, &context );
+        restore_context( &context, HANDLER_CONTEXT );
+    }
 }
 
 
diff --git a/dlls/ntdll/signal_sparc.c b/dlls/ntdll/signal_sparc.c
index 93871a8..5b02178 100644
--- a/dlls/ntdll/signal_sparc.c
+++ b/dlls/ntdll/signal_sparc.c
@@ -310,17 +310,21 @@
  */
 static void int_handler( int signal, siginfo_t *info, ucontext_t *ucontext )
 {
-    EXCEPTION_RECORD rec;
-    CONTEXT context;
+    extern int CONSOLE_HandleCtrlC(void);
+    if (!CONSOLE_HandleCtrlC())
+    {
+        EXCEPTION_RECORD rec;
+        CONTEXT context;
 
-    save_context( &context, ucontext );
-    rec.ExceptionCode    = CONTROL_C_EXIT;
-    rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
-    rec.ExceptionRecord  = NULL;
-    rec.ExceptionAddress = (LPVOID)context.pc;
-    rec.NumberParameters = 0;
-    EXC_RtlRaiseException( &rec, &context );
-    restore_context( &context, ucontext );
+        save_context( &context, ucontext );
+        rec.ExceptionCode    = CONTROL_C_EXIT;
+        rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
+        rec.ExceptionRecord  = NULL;
+        rec.ExceptionAddress = (LPVOID)context.pc;
+        rec.NumberParameters = 0;
+        EXC_RtlRaiseException( &rec, &context );
+        restore_context( &context, ucontext );
+    }
 }
 
 
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index a769789..c5d991a 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -1366,6 +1366,7 @@
 };
 
 
+
 struct move_console_output_request
 {
     struct request_header __header;
@@ -1384,6 +1385,19 @@
 
 
 
+struct send_console_signal_request
+{
+    struct request_header __header;
+    int          signal;
+    void*        group_id;
+};
+struct send_console_signal_reply
+{
+    struct reply_header __header;
+};
+
+
+
 struct create_change_notification_request
 {
     struct request_header __header;
@@ -2784,6 +2798,7 @@
     REQ_fill_console_output,
     REQ_read_console_output,
     REQ_move_console_output,
+    REQ_send_console_signal,
     REQ_create_change_notification,
     REQ_create_mapping,
     REQ_open_mapping,
@@ -2946,6 +2961,7 @@
     struct fill_console_output_request fill_console_output_request;
     struct read_console_output_request read_console_output_request;
     struct move_console_output_request move_console_output_request;
+    struct send_console_signal_request send_console_signal_request;
     struct create_change_notification_request create_change_notification_request;
     struct create_mapping_request create_mapping_request;
     struct open_mapping_request open_mapping_request;
@@ -3106,6 +3122,7 @@
     struct fill_console_output_reply fill_console_output_reply;
     struct read_console_output_reply read_console_output_reply;
     struct move_console_output_reply move_console_output_reply;
+    struct send_console_signal_reply send_console_signal_reply;
     struct create_change_notification_reply create_change_notification_reply;
     struct create_mapping_reply create_mapping_reply;
     struct open_mapping_reply open_mapping_reply;
@@ -3192,6 +3209,6 @@
     struct get_window_properties_reply get_window_properties_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 80
+#define SERVER_PROTOCOL_VERSION 81
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/console.c b/server/console.c
index 6ddd077..718d124 100644
--- a/server/console.c
+++ b/server/console.c
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <signal.h>
 
 #include "handle.h"
 #include "process.h"
@@ -385,6 +386,54 @@
     return console->recnum ? 1 : 0;
 }
 
+struct console_signal_info {
+    struct console_input        *console;
+    struct process              *group;
+    int                          signal;
+};
+
+static int propagate_console_signal_cb(struct process *process, void *user)
+{
+    struct console_signal_info* csi = (struct console_signal_info*)user;
+
+    if (process->console == csi->console && process->running_threads &&
+        (csi->group == NULL || process->group_id == csi->group))
+    {
+        struct thread *thread = process->thread_list;
+
+        while (thread)
+        {
+            struct thread *next = thread->proc_next;
+            kill( thread->unix_pid, csi->signal );
+            thread = next;
+        }
+    }
+    return FALSE;
+}
+
+static void propagate_console_signal( struct console_input *console, 
+                                      int sig, void* group_id )
+{
+    struct console_signal_info csi;
+
+    if (!console)
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        return;
+    }
+    /* FIXME: should support the other events (like CTRL_BREAK) */
+    if (sig != CTRL_C_EVENT)
+    {
+        set_error( STATUS_NOT_IMPLEMENTED );
+        return;
+    }
+    csi.console = console;
+    csi.signal  = SIGINT;
+    csi.group   = group_id;
+
+    enum_processes(propagate_console_signal_cb, &csi);
+}
+
 static int get_console_mode( obj_handle_t handle )
 {
     struct object *obj;
@@ -443,8 +492,31 @@
     }
     console->records = new_rec;
     memcpy( new_rec + console->recnum, records, count * sizeof(INPUT_RECORD) );
-    console->recnum += count;
 
+    if (console->mode & ENABLE_PROCESSED_INPUT)
+    {
+        int i = 0;
+        while (i < count)
+        {
+            if (records[i].EventType == KEY_EVENT && 
+		records[i].Event.KeyEvent.uChar.UnicodeChar == 'C' - 64 &&
+		!(records[i].Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
+            {
+                if (i != count - 1)
+                    memcpy( &console->records[console->recnum + i], 
+                            &console->records[console->recnum + i + 1], 
+                            (count - i - 1) * sizeof(INPUT_RECORD) );
+                count--;
+                if (records[i].Event.KeyEvent.bKeyDown)
+                {
+                    /* send SIGINT to all processes attached to this console */
+                    propagate_console_signal( console, CTRL_C_EVENT, NULL );
+                }
+            }
+            else i++;
+        }
+    }
+    console->recnum += count;
     /* wake up all waiters */
     wake_up( &console->obj, 0 );
     return count;
@@ -1385,3 +1457,18 @@
     scroll_console_output( req->handle, req->x_src, req->y_src, req->x_dst, req->y_dst,
 			   req->w, req->h );
 }
+
+/* sends a signal to a console (process, group...) */
+DECL_HANDLER(send_console_signal)
+{
+    void*       group;
+
+    group = req->group_id ? req->group_id : current->process->group_id;
+
+    if (!group)
+        set_error( STATUS_INVALID_PARAMETER);
+    else
+        propagate_console_signal( current->process->console, req->signal, group );
+}
+
+
diff --git a/server/process.c b/server/process.c
index 0f89aa3..6a60569 100644
--- a/server/process.c
+++ b/server/process.c
@@ -218,6 +218,7 @@
     process->exe.dbg_size    = 0;
     process->exe.namelen     = 0;
     process->exe.filename    = NULL;
+    process->group_id        = NULL;
 
     gettimeofday( &process->start_time, NULL );
     if ((process->next = first_process) != NULL) process->next->prev = process;
@@ -285,6 +286,7 @@
     /* set the process console */
     if (!set_process_console( process, parent_thread, info, reply )) return NULL;
 
+    process->group_id = process;
     if (parent)
     {
         /* attach to the debugger if requested */
@@ -292,6 +294,8 @@
             set_process_debugger( process, parent_thread );
         else if (parent->debugger && !(parent->create_flags & DEBUG_ONLY_THIS_PROCESS))
             set_process_debugger( process, parent->debugger );
+        if (!(process->create_flags & CREATE_NEW_PROCESS_GROUP))
+            process->group_id = parent->group_id;
     }
 
     /* thread will be actually suspended in init_done */
@@ -614,6 +618,16 @@
 }
 
 
+void enum_processes( int (*cb)(struct process*, void*), void *user )
+{
+    struct process *process;
+    for (process = first_process; process; process = process->next)
+    {
+        if ((cb)(process, user)) break;
+    }
+}
+
+
 /* get all information about a process */
 static void get_process_info( struct process *process, struct get_process_info_reply *reply )
 {
diff --git a/server/process.h b/server/process.h
index e320491..27ea25f 100644
--- a/server/process.h
+++ b/server/process.h
@@ -72,6 +72,7 @@
     struct process_dll   exe;             /* main exe file */
     void                *ldt_copy;        /* pointer to LDT copy in client addr space */
     void                *ldt_flags;       /* pointer to LDT flags in client addr space */
+    void                *group_id;        /* group ID of the process */
 };
 
 struct process_snapshot
@@ -110,6 +111,7 @@
 extern void detach_debugged_processes( struct thread *debugger );
 extern struct process_snapshot *process_snap( int *count );
 extern struct module_snapshot *module_snap( struct process *process, int *count );
+extern void enum_processes( int (*cb)(struct process*, void*), void *user);
 
 inline static void *get_process_id( struct process *process ) { return process; }
 inline static int is_process_init_done( struct process *process )
diff --git a/server/protocol.def b/server/protocol.def
index 8b68a43..9d4f8f3 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1009,6 +1009,7 @@
     VARARG(data,bytes);
 @END
 
+
 /* move a rect (of data) in screen buffer content */
 @REQ(move_console_output)
     obj_handle_t handle;        /* handle to the console output */
@@ -1021,6 +1022,13 @@
 @END
 
 
+/* Sends a signal to a process group */
+@REQ(send_console_signal)
+    int          signal;        /* the signal to send */
+    void*        group_id;      /* the group to send the signal to */
+@END
+
+
 /* Create a change notification */
 @REQ(create_change_notification)
     int          subtree;       /* watch all the subtree */
diff --git a/server/request.h b/server/request.h
index 94a659d..5f893ab 100644
--- a/server/request.h
+++ b/server/request.h
@@ -172,6 +172,7 @@
 DECL_HANDLER(fill_console_output);
 DECL_HANDLER(read_console_output);
 DECL_HANDLER(move_console_output);
+DECL_HANDLER(send_console_signal);
 DECL_HANDLER(create_change_notification);
 DECL_HANDLER(create_mapping);
 DECL_HANDLER(open_mapping);
@@ -333,6 +334,7 @@
     (req_handler)req_fill_console_output,
     (req_handler)req_read_console_output,
     (req_handler)req_move_console_output,
+    (req_handler)req_send_console_signal,
     (req_handler)req_create_change_notification,
     (req_handler)req_create_mapping,
     (req_handler)req_open_mapping,
diff --git a/server/trace.c b/server/trace.c
index b36cfa7..c96863d 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1191,6 +1191,12 @@
     fprintf( stderr, " h=%d", req->h );
 }
 
+static void dump_send_console_signal_request( const struct send_console_signal_request *req )
+{
+    fprintf( stderr, " signal=%d,", req->signal );
+    fprintf( stderr, " group_id=%p", req->group_id );
+}
+
 static void dump_create_change_notification_request( const struct create_change_notification_request *req )
 {
     fprintf( stderr, " subtree=%d,", req->subtree );
@@ -2239,6 +2245,7 @@
     (dump_func)dump_fill_console_output_request,
     (dump_func)dump_read_console_output_request,
     (dump_func)dump_move_console_output_request,
+    (dump_func)dump_send_console_signal_request,
     (dump_func)dump_create_change_notification_request,
     (dump_func)dump_create_mapping_request,
     (dump_func)dump_open_mapping_request,
@@ -2397,6 +2404,7 @@
     (dump_func)dump_fill_console_output_reply,
     (dump_func)dump_read_console_output_reply,
     (dump_func)0,
+    (dump_func)0,
     (dump_func)dump_create_change_notification_reply,
     (dump_func)dump_create_mapping_reply,
     (dump_func)dump_open_mapping_reply,
@@ -2555,6 +2563,7 @@
     "fill_console_output",
     "read_console_output",
     "move_console_output",
+    "send_console_signal",
     "create_change_notification",
     "create_mapping",
     "open_mapping",
diff --git a/win32/console.c b/win32/console.c
index 1c21889..a98c971 100644
--- a/win32/console.c
+++ b/win32/console.c
@@ -5,7 +5,7 @@
  * Copyright 1997 Karl Garrison
  * Copyright 1998 John Richardson
  * Copyright 1998 Marcus Meissner
- * Copyright 2001 Eric Pouech
+ * Copyright 2001,2002 Eric Pouech
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -109,7 +109,7 @@
     }
 
     /* then try the regular PATH */
-    sprintf(buffer, "wineconsole --use-event=%d\n", hEvent);
+    sprintf(buffer, "wineconsole --use-event=%d", hEvent);
     if (CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
 	goto succeed;
 
@@ -210,7 +210,6 @@
 {
     BOOL	ret;
     unsigned	read = 0;
-    DWORD	mode;
 
     SERVER_START_REQ( read_console_input )
     {
@@ -220,22 +219,6 @@
         if ((ret = !wine_server_call_err( req ))) read = reply->read;
     }
     SERVER_END_REQ;
-    if (count && flush && GetConsoleMode(handle, &mode) && (mode & ENABLE_PROCESSED_INPUT))
-    {
-	int	i;
-
-	for (i = 0; i < read; i++)
-	{
-	    if (buffer[i].EventType == KEY_EVENT && buffer[i].Event.KeyEvent.bKeyDown &&
-		buffer[i].Event.KeyEvent.uChar.UnicodeChar == 'C' - 64 &&
-		!(buffer[i].Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
-	    {
-		GenerateConsoleCtrlEvent(CTRL_C_EVENT, GetCurrentProcessId());
-		/* FIXME: this is hackish, but it easily disables IR handling afterwards */
-		buffer[i].Event.KeyEvent.uChar.UnicodeChar = 0;
-	    }
-	}
-    }
     if (pRead) *pRead = read;
     return ret;
 }
@@ -460,6 +443,11 @@
     return ret;
 }
 
+/******************************************************************
+ *		CONSOLE_DefaultHandler
+ *
+ * Final control event handler
+ */
 static BOOL WINAPI CONSOLE_DefaultHandler(DWORD dwCtrlType)
 {
     FIXME("Terminating process %lx on event %lx\n", GetCurrentProcessId(), dwCtrlType);
@@ -546,9 +534,6 @@
  *    dwCtrlEvent        [I] Type of event
  *    dwProcessGroupID   [I] Process group ID to send event to
  *
- * NOTES
- *    Doesn't yet work...!
- *
  * RETURNS
  *    Success: True
  *    Failure: False (and *should* [but doesn't] set LastError)
@@ -556,46 +541,25 @@
 BOOL WINAPI GenerateConsoleCtrlEvent(DWORD dwCtrlEvent,
 				     DWORD dwProcessGroupID)
 {
+    BOOL ret;
+
+    TRACE("(%ld, %ld)\n", dwCtrlEvent, dwProcessGroupID);
+
     if (dwCtrlEvent != CTRL_C_EVENT && dwCtrlEvent != CTRL_BREAK_EVENT)
     {
-	ERR("invalid event %ld for PGID %ld\n", dwCtrlEvent, dwProcessGroupID);
+	ERR("Invalid event %ld for PGID %ld\n", dwCtrlEvent, dwProcessGroupID);
 	return FALSE;
     }
 
-    if (dwProcessGroupID == GetCurrentProcessId() || dwProcessGroupID == 0)
+    SERVER_START_REQ( send_console_signal )
     {
-	int	i;
-
-	FIXME("Attempt to send event %ld to self groupID, doing locally only\n", dwCtrlEvent);
-
-	/* this is only meaningfull when done locally, otherwise it will have to be done on
-	 * the 'receive' side of the event generation
-	 */
-	if (dwCtrlEvent == CTRL_C_EVENT && console_ignore_ctrl_c)
-	    return TRUE;
-
-        /* try to pass the exception to the debugger
-         * if it continues, there's nothing more to do
-         * otherwise, we need to send the ctrl-event to the handlers
-         */
-        __TRY
-        {
-            RaiseException( (dwCtrlEvent == CTRL_C_EVENT) ? DBG_CONTROL_C : DBG_CONTROL_BREAK,
-                            0, 0, NULL);
-        }
-        __EXCEPT(CONSOLE_CtrlEventHandler)
-        {
-            /* the debugger didn't continue... so, pass to ctrl handlers */
-            for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++)
-            {
-                if (handlers[i] && (handlers[i])(dwCtrlEvent)) break;
-            }
-        }
-        __ENDTRY;
-        return TRUE;
+        req->signal = dwCtrlEvent;
+        req->group_id = (void*)dwProcessGroupID;
+        ret = !wine_server_call_err( req );
     }
-    FIXME("event %ld to external PGID %ld - not implemented yet\n", dwCtrlEvent, dwProcessGroupID);
-    return FALSE;
+    SERVER_END_REQ;
+
+    return ret;
 }
 
 
@@ -1346,6 +1310,7 @@
  * Console manipulation functions
  *
  * ====================================================================*/
+
 /* some missing functions...
  * FIXME: those are likely to be defined as undocumented function in kernel32 (or part of them)
  * should get the right API and implement them
@@ -1405,7 +1370,7 @@
  */
 unsigned CONSOLE_GetNumHistoryEntries(void)
 {
-    unsigned ret = 0;
+    unsigned ret = -1;
     SERVER_START_REQ(get_console_input_info)
     {
         req->handle = 0;
@@ -1415,3 +1380,40 @@
     return ret;
 }
 
+/******************************************************************
+ *		CONSOLE_HandleCtrlC
+ *
+ * Check whether the shall manipulate CtrlC events
+ */
+int     CONSOLE_HandleCtrlC(void)
+{
+    int i;
+
+    /* FIXME: better test whether a console is attached to this process ??? */
+    extern    unsigned CONSOLE_GetNumHistoryEntries(void);
+    if (CONSOLE_GetNumHistoryEntries() == (unsigned)-1) return 0;
+    
+    /* try to pass the exception to the debugger
+     * if it continues, there's nothing more to do
+     * otherwise, we need to send the ctrl-event to the handlers
+     */
+    __TRY
+    {
+        RaiseException( DBG_CONTROL_C, 0, 0, NULL );
+    }
+    __EXCEPT(CONSOLE_CtrlEventHandler)
+    {
+        /* the debugger didn't continue... so, pass to ctrl handlers */
+        /* FIXME: since this routine is called while in a signal handler, 
+         * there are some serious synchronisation issues with
+         * SetConsoleCtrlHandler (trouble ahead)
+         */
+        for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++)
+        {
+            if (handlers[i] && (handlers[i])(CTRL_C_EVENT)) break;
+        }
+    }
+    __ENDTRY;
+    return 1;
+}
+