macemu/SheepShaver/src/BeOS/clip_beos.cpp
CharlesJS ed28705ee3 Patch for copying and pasting styled text in Basilisk II / SheepShaver
Added code to parse the Classic Mac OS 'styl' resources, allowing formatted text to be copied and pasted out of SheepShaver, not just plain text. In order to do this, I made some changes to the emul_op mechanism, patching ZeroScrap() in addition to the scrap methods that were already being patched. The reason for this is that since we need to read data from multiple items that are on the clipboard at once, we cannot simply assume a zero at the beginning of each PutScrap() operation.

This patch uses RTF to store styled text on the host side; unfortunately, since the APIs to convert to and from RTF data are in Cocoa but not in CoreFoundation, I had to write the new portions in Objective-C rather than C, and changed the extension from .cpp to .mm accordingly. In the future, if we are confident that this file will only be used on Mac OS X 10.6 and up, we can rewrite the Pasteboard Manager code to use NSPasteboardReading/Writing instead. This would allow us to read and write NSAttributedString objects directly to and from the pasteboard, which would make sure we were always using the OS's preferred rich text format internally instead of hard-coding it specifically to RTF as in the current implementation.

I believe that this patch should also fix the problem Ronald reported with copying accented characters.

Since I am new to 68k assembly and the emul_op mechanism, I would appreciate if someone could double-check all my changes to make sure that I have done everything correctly.

Thanks,
Charles
2012-06-30 22:20:55 -04:00

375 lines
11 KiB
C++

/*
* clip_beos.cpp - Clipboard handling, BeOS implementation
*
* SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <support/UTF8.h>
#include "clip.h"
#include "main.h"
#include "cpu_emulation.h"
#include "emul_op.h"
#define DEBUG 0
#include "debug.h"
// Global variables
static bool we_put_this_data = false; // Flag for PutScrap(): the data was put by GetScrap(), don't bounce it back to the Be side
static BTranslatorRoster *roster;
static float input_cap = 0;
static translator_info input_info;
static float output_cap = 0;
static translator_id output_trans = 0;
/*
* Clipboard manager thread (for calling clipboard functions; this is not safe
* under R4 when running on the MacOS stack in kernel space)
*/
// Message constants
const uint32 MSG_QUIT_CLIP_MANAGER = 'quit';
const uint32 MSG_PUT_TEXT = 'ptxt';
static thread_id cm_thread = -1;
static sem_id cm_done_sem = -1;
// Argument passing
static void *cm_scrap;
static int32 cm_length;
static status_t clip_manager(void *arg)
{
for (;;) {
// Receive message
thread_id sender;
uint32 code = receive_data(&sender, NULL, 0);
D(bug("Clipboard manager received %08lx\n", code));
switch (code) {
case MSG_QUIT_CLIP_MANAGER:
return 0;
case MSG_PUT_TEXT:
if (be_clipboard->Lock()) {
be_clipboard->Clear();
BMessage *clipper = be_clipboard->Data();
// Convert text from Mac charset to UTF-8
int32 dest_length = cm_length * 3;
int32 state = 0;
char *inbuf = new char[cm_length];
memcpy(inbuf, cm_scrap, cm_length); // Copy to user space
char *outbuf = new char[dest_length];
if (convert_to_utf8(B_MAC_ROMAN_CONVERSION, inbuf, &cm_length, outbuf, &dest_length, &state) == B_OK) {
for (int i=0; i<dest_length; i++)
if (outbuf[i] == 13)
outbuf[i] = 10;
// Add text to Be clipboard
clipper->AddData("text/plain", B_MIME_TYPE, outbuf, dest_length);
be_clipboard->Commit();
} else {
D(bug(" text conversion failed\n"));
}
delete[] outbuf;
delete[] inbuf;
be_clipboard->Unlock();
}
break;
}
// Acknowledge
release_sem(cm_done_sem);
}
}
/*
* Initialize clipboard
*/
void ClipInit(void)
{
// check if there is a translator that can handle the pict datatype
roster = BTranslatorRoster::Default();
int32 num_translators, i,j;
translator_id *translators;
const char *translator_name, *trans_info;
int32 translator_version;
const translation_format *t_formats;
long t_num;
roster->GetAllTranslators(&translators, &num_translators);
for (i=0;i<num_translators;i++) {
roster->GetTranslatorInfo(translators[i], &translator_name,
&trans_info, &translator_version);
D(bug("found translator %s: %s (%.2f)\n", translator_name, trans_info,
translator_version/100.));
// does this translator support the pict datatype ?
roster->GetInputFormats(translators[i], &t_formats,&t_num);
//printf(" supports %d input formats \n",t_num);
for (j=0;j<t_num;j++) {
if (!strcmp (t_formats[j].MIME,"image/pict")) {
// matching translator found
if (t_formats[j].capability>input_cap) {
input_info.type = t_formats[j].type;
input_info.group = t_formats[j].group;
input_info.quality = t_formats[j].quality;
input_info.capability = t_formats[j].capability;
strcpy(input_info.MIME,t_formats[j].MIME);
strcpy(input_info.name,t_formats[j].name);
input_info.translator=translators[i];
input_cap = t_formats[j].capability;
}
D(bug("matching input translator found:type:%c%c%c%c group:%c%c%c%c quality:%f capability:%f MIME:%s name:%s\n",
t_formats[j].type>>24,t_formats[j].type>>16,t_formats[j].type>>8,t_formats[j].type,
t_formats[j].group>>24,t_formats[j].group>>16,t_formats[j].group>>8,t_formats[j].group,
t_formats[j].quality,
t_formats[j].capability,t_formats[j].MIME,
t_formats[j].name));
}
}
roster->GetOutputFormats(translators[i], &t_formats,&t_num);
//printf("and %d output formats \n",t_num);
for (j=0;j<t_num;j++) {
if (!strcmp (t_formats[j].MIME,"image/pict")) {
if (t_formats[j].capability>output_cap) {
output_trans = translators[i];
output_cap = t_formats[j].capability;
}
D(bug("matching output translator found:type:%c%c%c%c group:%c%c%c%c quality:%f capability:%f MIME:%s name:%s\n",
t_formats[j].type>>24,t_formats[j].type>>16,t_formats[j].type>>8,t_formats[j].type,
t_formats[j].group>>24,t_formats[j].group>>16,t_formats[j].group>>8,t_formats[j].group,
t_formats[j].quality,
t_formats[j].capability,t_formats[j].MIME,
t_formats[j].name));
}
}
}
delete [] translators; // clean up our droppings
// Start clipboard manager thread
cm_done_sem = create_sem(0, "Clipboard Manager Done");
cm_thread = spawn_thread(clip_manager, "Clipboard Manager", B_NORMAL_PRIORITY, NULL);
resume_thread(cm_thread);
}
/*
* Deinitialize clipboard
*/
void ClipExit(void)
{
// Stop clipboard manager
if (cm_thread > 0) {
status_t l;
send_data(cm_thread, MSG_QUIT_CLIP_MANAGER, NULL, 0);
while (wait_for_thread(cm_thread, &l) == B_INTERRUPTED) ;
}
// Delete semaphores
delete_sem(cm_done_sem);
}
/*
* Mac application wrote to clipboard
*/
void PutScrap(uint32 type, void *scrap, int32 length)
{
D(bug("PutScrap type %08lx, data %p, length %ld\n", type, scrap, length));
if (we_put_this_data) {
we_put_this_data = false;
return;
}
if (length <= 0)
return;
switch (type) {
case 'TEXT':
D(bug(" clipping TEXT\n"));
cm_scrap = scrap;
cm_length = length;
while (send_data(cm_thread, MSG_PUT_TEXT, NULL, 0) == B_INTERRUPTED) ;
while (acquire_sem(cm_done_sem) == B_INTERRUPTED) ;
break;
case 'PICT':
D(bug(" clipping PICT\n"));
//!! this has to be converted to use the Clipboard Manager
#if 0
if (be_clipboard->Lock()) {
be_clipboard->Clear();
BMessage *clipper = be_clipboard->Data();
// Waaaah! This crashes!
if (input_cap > 0) { // if there is an converter for PICT datatype convert data to bitmap.
BMemoryIO *in_buffer = new BMemoryIO(scrap, length);
BMallocIO *out_buffer = new BMallocIO();
status_t result=roster->Translate(in_buffer,&input_info,NULL,out_buffer,B_TRANSLATOR_BITMAP);
clipper->AddData("image/x-be-bitmap", B_MIME_TYPE, out_buffer->Buffer(), out_buffer->BufferLength());
D(bug("conversion result:%08x buffer_size:%d\n",result,out_buffer->BufferLength()));
delete in_buffer;
delete out_buffer;
}
clipper->AddData("image/pict", B_MIME_TYPE, scrap, length);
be_clipboard->Commit();
be_clipboard->Unlock();
}
#endif
break;
}
}
/*
* Mac application zeroes clipboard
*/
void ZeroScrap()
{
}
/*
* Mac application reads clipboard
*/
void GetScrap(void **handle, uint32 type, int32 offset)
{
M68kRegisters r;
D(bug("GetScrap handle %p, type %08lx, offset %ld\n", handle, type, offset));
return; //!! GetScrap is currently broken (should use Clipboard Manager)
//!! replace with clipboard notification in BeOS R4.1
switch (type) {
case 'TEXT':
D(bug(" clipping TEXT\n"));
if (be_clipboard->Lock()) {
BMessage *clipper = be_clipboard->Data();
char *clip;
ssize_t length;
// Check if we already copied this data
if (clipper->HasData("application/x-SheepShaver-cookie", B_MIME_TYPE))
return;
bigtime_t cookie = system_time();
clipper->AddData("application/x-SheepShaver-cookie", B_MIME_TYPE, &cookie, sizeof(bigtime_t));
// No, is there text in it?
if (clipper->FindData("text/plain", B_MIME_TYPE, &clip, &length) == B_OK) {
D(bug(" text/plain found\n"));
// Convert text from UTF-8 to Mac charset
int32 src_length = length;
int32 dest_length = length;
int32 state = 0;
char *outbuf = new char[dest_length];
if (convert_from_utf8(B_MAC_ROMAN_CONVERSION, clip, &src_length, outbuf, &dest_length, &state) == B_OK) {
for (int i=0; i<dest_length; i++)
if (outbuf[i] == 10)
outbuf[i] = 13;
// Add text to Mac clipboard
static uint16 proc[] = {
0x598f, // subq.l #4,sp
0xa9fc, // ZeroScrap()
0x2f3c, 0, 0, // move.l #length,-(sp)
0x2f3c, 'TE', 'XT', // move.l #'TEXT',-(sp)
0x2f3c, 0, 0, // move.l #outbuf,-(sp)
0xa9fe, // PutScrap()
0x588f, // addq.l #4,sp
M68K_RTS
};
*(int32 *)(proc + 3) = dest_length;
*(char **)(proc + 9) = outbuf;
we_put_this_data = true;
Execute68k((uint32)proc, &r);
} else {
D(bug(" text conversion failed\n"));
}
delete[] outbuf;
}
be_clipboard->Commit();
be_clipboard->Unlock();
}
break;
case 'PICT':
D(bug(" clipping PICT\n"));
if (be_clipboard->Lock()) {
BMessage *clipper = be_clipboard->Data();
char *clip;
ssize_t length;
// Check if we already copied this data
if (clipper->HasData("application/x-SheepShaver-cookie", B_MIME_TYPE))
return;
bigtime_t cookie = system_time();
clipper->AddData("application/x-SheepShaver-cookie", B_MIME_TYPE, &cookie, sizeof(bigtime_t));
static uint16 proc2[] = {
0x598f, // subq.l #4,sp
0xa9fc, // ZeroScrap()
0x2f3c, 0, 0, // move.l #length,-(sp)
0x2f3c, 'PI', 'CT', // move.l #'PICT',-(sp)
0x2f3c, 0, 0, // move.l #buf,-(sp)
0xa9fe, // PutScrap()
0x588f, // addq.l #4,sp
M68K_RTS
};
// No, is there a pict ?
if (clipper->FindData("image/pict", B_MIME_TYPE, &clip, &length) == B_OK ) {
D(bug(" image/pict found\n"));
// Add pict to Mac clipboard
*(int32 *)(proc2 + 3) = length;
*(char **)(proc2 + 9) = clip;
we_put_this_data = true;
Execute68k((uint32)proc2, &r);
#if 0
// No, is there a bitmap ?
} else if (clipper->FindData("image/x-be-bitmap", B_MIME_TYPE, &clip, &length) == B_OK || output_cap > 0) {
D(bug(" image/x-be-bitmap found\nstarting conversion to PICT\n"));
BMemoryIO *in_buffer = new BMemoryIO(clip, length);
BMallocIO *out_buffer = new BMallocIO();
status_t result=roster->Translate(output_trans,in_buffer,NULL,out_buffer,'PICT');
D(bug("result of conversion:%08x buffer_size:%d\n",result,out_buffer->BufferLength()));
// Add pict to Mac clipboard
*(int32 *)(proc2 + 3) = out_buffer->BufferLength();
*(char **)(proc2 + 9) = (char *)out_buffer->Buffer();
we_put_this_data = true;
Execute68k(proc2, &r);
delete in_buffer;
delete out_buffer;
#endif
}
be_clipboard->Commit();
be_clipboard->Unlock();
}
break;
}
}