From 616ae26efa89b2c79af50cbe90a4e1190604fe8d Mon Sep 17 00:00:00 2001 From: Eric Petit Date: Thu, 12 Jan 2006 18:52:15 +0000 Subject: [PATCH] Update 2005-12-13 --- Jamfile | 5 + Jamrules | 19 +- beos/TRApplication.cpp | 298 +++++++++++++++++++++ beos/TRApplication.h | 50 ++++ beos/TRInfoWindow.cpp | 93 +++++++ beos/TRInfoWindow.h | 22 ++ beos/TRPrefsWindow.cpp | 165 ++++++++++++ beos/TRPrefsWindow.h | 34 +++ beos/TRTransfer.cpp | 235 +++++++++++++++++ beos/TRTransfer.h | 56 ++++ beos/TRWindow.cpp | 457 +++++++++++++++++++++++++++++++++ beos/TRWindow.h | 67 +++++ beos/Transmission.rsrc | Bin 0 -> 5628 bytes beos/libPrefs/Prefs.h | 33 +++ beos/libPrefs/libPrefs.a | Bin 0 -> 11116 bytes beos/makefile | 107 ++++++++ configure | 20 +- libtransmission/Jamfile | 2 +- libtransmission/platform.c | 59 +++++ libtransmission/platform.h | 42 +++ libtransmission/transmission.c | 17 +- libtransmission/transmission.h | 15 +- 22 files changed, 1787 insertions(+), 9 deletions(-) create mode 100644 beos/TRApplication.cpp create mode 100644 beos/TRApplication.h create mode 100644 beos/TRInfoWindow.cpp create mode 100644 beos/TRInfoWindow.h create mode 100644 beos/TRPrefsWindow.cpp create mode 100644 beos/TRPrefsWindow.h create mode 100644 beos/TRTransfer.cpp create mode 100644 beos/TRTransfer.h create mode 100644 beos/TRWindow.cpp create mode 100644 beos/TRWindow.h create mode 100644 beos/Transmission.rsrc create mode 100644 beos/libPrefs/Prefs.h create mode 100644 beos/libPrefs/libPrefs.a create mode 100644 beos/makefile create mode 100644 libtransmission/platform.c create mode 100644 libtransmission/platform.h diff --git a/Jamfile b/Jamfile index 343b27f1e..b09f916d8 100644 --- a/Jamfile +++ b/Jamfile @@ -50,4 +50,9 @@ else if $(GTK) = yes SubInclude TOP gtk ; } +if $(OS) = BEOS +{ + BeOSBuild Transmission : libtransmission.a ; +} + SubInclude TOP libtransmission ; diff --git a/Jamrules b/Jamrules index 5612cff3c..002b1a0c7 100644 --- a/Jamrules +++ b/Jamrules @@ -8,12 +8,12 @@ if ! $(DEFINES) VERSION_MAJOR = 0 ; VERSION_MINOR = 4 ; # VERSION_STRING = $(VERSION_MAJOR).$(VERSION_MINOR) ; -VERSION_STRING = CVS-20051124 ; +VERSION_STRING = CVS-20051213 ; DEFINES += VERSION_MAJOR=$(VERSION_MAJOR) VERSION_MINOR=$(VERSION_MINOR) VERSION_STRING=\\\"$(VERSION_STRING)\\\" ; -OPTIM = -O3 ; +OPTIM = -O0 ; RM = rm -Rf ; rule SystemLibraries @@ -93,3 +93,18 @@ if $(OS) = MACOSX rm -rf "$TMP" } } + +if $(OS) = BEOS +{ + rule BeOSBuild + { + Depends exe : $(1) ; + Depends $(1) : $(2) ; + Clean clean : $(1) beos/obj.$(CPU) ; + } + actions BeOSBuild + { + $(RM) $(1) && ( cd beos && make ) && \ + mv beos/obj.$(CPU)/Transmission $(1) + } +} \ No newline at end of file diff --git a/beos/TRApplication.cpp b/beos/TRApplication.cpp new file mode 100644 index 000000000..b9816de60 --- /dev/null +++ b/beos/TRApplication.cpp @@ -0,0 +1,298 @@ +#include "TRApplication.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + TRApplication *app = new TRApplication(); + if (app->InitCheck() == B_OK) { + app->Run(); + } else { + BString errMsg(""); + errMsg << "The following error occurred loading Transmission:\n" + << strerror(app->InitCheck()); + BAlert *ohNo = new BAlert("Transmission Error", + errMsg.String(), + "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); + ohNo->Go(); + } + delete app; + + return (0); +} + + +TRApplication::TRApplication() : BApplication(APP_SIG) { + status_t err = B_OK; + torrentDir = NULL; + + // Install Mime types if necessary. + BMimeType torrentType("application/x-bittorrent"); + if (torrentType.InitCheck() == B_OK) { + if (!torrentType.IsInstalled()) { + fprintf(stderr, "Installing mime type...\n"); + // Icon + app_info runningInfo; + GetAppInfo(&runningInfo); + BFile appFile(&(runningInfo.ref), B_READ_ONLY); + BResources res(&appFile, false); + + size_t len = 0; + BBitmap *icon = NULL; + void *iconBits = NULL; + + iconBits = res.FindResource('ICON', "BEOS:L:application/x-bittorrent", &len); + if (iconBits) { + icon = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8); + icon->SetBits(iconBits, len, 0, B_CMAP8); + torrentType.SetIcon(icon, B_LARGE_ICON); + delete icon; + icon = NULL; + len = 0; + } + + iconBits = res.FindResource('ICON', "BEOS:M:application/x-bittorrent", &len); + if (iconBits) { + icon = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8); + icon->SetBits(iconBits, len, 0, B_CMAP8); + torrentType.SetIcon(icon, B_MINI_ICON); + delete icon; + } + + // Extensions + BMessage extensions; + extensions.AddString("extensions", "torrent"); + torrentType.SetFileExtensions(&extensions); + + torrentType.SetShortDescription("BitTorrent Meta File"); + torrentType.SetLongDescription("BitTorrent Protocol Meta File (http://www.bittorrent.com)"); + + // Set Preferred Application + torrentType.SetPreferredApp(APP_SIG, B_OPEN); + + torrentType.Install(); + } + } + + // Create Settings folder and structure + BPath settingsPath; + err = find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath, false); + if (err == B_OK) { + BDirectory settings(settingsPath.Path()); + BDirectory *trSettings = new BDirectory(); + + err = settings.CreateDirectory("Transmission", trSettings); + if (err == B_FILE_EXISTS) { + err = trSettings->SetTo(&settings, "Transmission"); + } + + if (err == B_OK) { + torrentDir = new BDirectory(); + err = trSettings->CreateDirectory("Torrents", torrentDir); + if (err != B_OK) { + torrentDir->SetTo(trSettings, "Torrents"); + } + } + delete trSettings; + } + + // Create window + window = new TRWindow(); + window->RescanTorrents(); + settings = new TRPrefsWindow(); + + openPanel = new BFilePanel(); + openPanel->SetRefFilter(new TRFilter()); +} + + +TRApplication::~TRApplication() { + if (window->Lock()) { + window->Quit(); + } + if (settings->Lock()) { + settings->Quit(); + } + + if (openPanel != NULL) { + delete openPanel; + } + + if (torrentDir != NULL) { + delete torrentDir; + } +} + + +/** + * Checks to make sure our Settings directory structure is in place. + * If this fails, then we need to display an alert and vomit. + */ +status_t TRApplication::InitCheck() { + if (torrentDir != NULL) { + return torrentDir->InitCheck(); + } + return B_ERROR; +} + + +void TRApplication::AboutRequested() { + // Read the application version info + app_info runningInfo; + GetAppInfo(&runningInfo); + BFile appFile(&(runningInfo.ref), B_READ_ONLY); + BAppFileInfo appInfo(&appFile); + version_info vInfo; + appInfo.GetVersionInfo(&vInfo, B_APP_VERSION_KIND); + + BString aboutMsg(""); + aboutMsg << "Transmission\n" + << "Version " << vInfo.major << "." << vInfo.middle << "." << vInfo.minor << "\n" + << "Eric Petit & Bryan Varner\n"; + BAlert *aboutBox = new BAlert("About Transmission", aboutMsg.String(), "Close"); + aboutBox->Go(); +} + + +void TRApplication::ReadyToRun() { + SetPulseRate(500000); + window->Show(); +} + + +void TRApplication::Pulse() { + window->UpdateList(-1, false); +} + + +/** + * When a ref is received, we copy each file that is to be opened into + * B_USER_SETTINGS/Transmission/Torrents + * + * Our window node_monitors this folder for added / removed torrent meta files. + * + * Each copy is performed in a seperate thread to avoid blocking / locking the app. + */ +void TRApplication::RefsReceived(BMessage *message) { + int32 count; + type_code code; + message->GetInfo("refs", &code, &count); + for (int i = 0; i < count; i++) { + entry_ref *ref = (entry_ref*)calloc(sizeof(entry_ref), 1); + if (message->FindRef("refs", i, ref) == B_OK) { + thread_id cp_thread = spawn_thread(TRApplication::Copy, "TorrentDuper", + B_NORMAL_PRIORITY, (void *)ref); + if (!((cp_thread) < B_OK)) { + resume_thread(cp_thread); + } + } + } +} + +/** + * BMessage handling. + */ +void TRApplication::MessageReceived(BMessage *message) { + BApplication::MessageReceived(message); + /* + * When the copy of a torrent file is complete, we get a message + * signaling that we should add the now copied .torrent file to our Transfer Window. + */ + if (message->what == TR_ADD) { + // Add the torrent to the window. + entry_ref torrentRef; + if (message->FindRef("torrent", &torrentRef) == B_OK) { + BEntry *entry = new BEntry(&torrentRef, true); + window->AddEntry(entry); + delete entry; + } + /* Show the Open Dialog */ + } else if (message->what == TR_OPEN) { + openPanel->Show(); + } else if (message->what == TR_SETTINGS) { + settings->MoveTo(window->Frame().left + (window->Frame().Width() - settings->Frame().Width()) / 2, + window->Frame().top + (window->Frame().Height() - settings->Frame().Height()) / 2); + + settings->Show(); + } else if (message->what == TR_RELOAD_SETTINGS) { + window->LoadSettings(); + } +} + +bool TRApplication::QuitRequested() { + return BApplication::QuitRequested(); +} + +/** + * Thread Function. + * The thread copies the original .torrent file into the torrents folder, + * then signals the window to load the torrent meta info from the copy. + * The torrent window will then node_monitor the file so that if it's removed + * the torrent will cease. + * + * Keeping the file copy in a separate thread keeps us from blocking up the + * rest of our program. + * + * This behavior lets us kill the original .torrent download from disc (which + * speaking as a user I have a tendency to do) but keep the meta around for + * Transmission to use later. + * + * If the user whacks the .torrent file from our settings folder, then we'll + * remove it from the list (node_monitor!) and if they remove it from our list + * we'll remove it from the file system. + */ +int32 TRApplication::Copy(void *data) { + entry_ref *ref = (entry_ref*)data; + + BFile source(ref, B_READ_ONLY); + BFile target(((TRApplication*)be_app)->TorrentDir(), ref->name, + B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS); + + BEntry targetEntry(((TRApplication*)be_app)->TorrentDir(), ref->name); + entry_ref targetRef; + targetEntry.GetRef(&targetRef); + + // Only perform the copy if the target is freshly created. Everything is B_OK! + if (source.InitCheck() == B_OK && target.InitCheck() == B_OK) { + if (target.Lock() == B_OK) { + char *buffer = (char*)calloc(1, 4096); // 4k data buffer. + ssize_t read = 0; + while ((read = source.Read(buffer, 4096)) > 0) { + target.Write(buffer, read); + } + free(buffer); + target.Unlock(); + } + } + + BMessage msg(TR_ADD); + msg.AddRef("torrent", &targetRef); + BMessenger messenger(be_app); + messenger.SendMessage(&msg); + + free(ref); + return B_OK; +} + +/** + * Filters the FilePanel for torrent files and directories. + */ +bool TRFilter::Filter(const entry_ref *ref, BNode *node, struct stat *st, const char *mimetype) { + return (node->IsDirectory() || + (strcmp(mimetype, "application/x-bittorrent") == 0) || + (strstr(ref->name, ".torrent") != NULL)); + +} diff --git a/beos/TRApplication.h b/beos/TRApplication.h new file mode 100644 index 000000000..9d72e866d --- /dev/null +++ b/beos/TRApplication.h @@ -0,0 +1,50 @@ +#ifndef TR_APP +#define TR_APP + +#include +#include +#include +#include + +#include "TRWindow.h" + +#define APP_SIG "application/x-vnd.titer-Transmission" +#define TRANSMISSION_SETTINGS "Transmission/settings" + + +#define TR_ADD 'tAdd' +#define TR_OPEN 'tOpn' +#define TR_RELOAD_SETTINGS 'tRSt' + +class TRApplication : public BApplication { + public: + TRApplication(); + ~TRApplication(); + + virtual void AboutRequested(); + virtual void Pulse(); + virtual void ReadyToRun(); + virtual void RefsReceived(BMessage *message); + virtual bool QuitRequested(); + + virtual void MessageReceived(BMessage *message); + + static int32 Copy(void *data); + + status_t InitCheck(); + inline BDirectory* TorrentDir() { return torrentDir; }; + private: + TRWindow *window; + TRPrefsWindow *settings; + BFilePanel *openPanel; + BDirectory *torrentDir; +}; + +/** Torrent File-Type Filter */ +class TRFilter : public BRefFilter { +public: + virtual bool Filter(const entry_ref *ref, BNode *node, + struct stat *st, const char *mimetype); +}; + +#endif /* TR_APP */ \ No newline at end of file diff --git a/beos/TRInfoWindow.cpp b/beos/TRInfoWindow.cpp new file mode 100644 index 000000000..41a339a55 --- /dev/null +++ b/beos/TRInfoWindow.cpp @@ -0,0 +1,93 @@ +#include "TRInfoWindow.h" + +#include +#include +#include +#include + +#include +#include + + +TRInfoWindow::TRInfoWindow(tr_stat_t status) : BWindow(BRect(0, 0, 250, 175), "Info", + B_FLOATING_WINDOW, B_ASYNCHRONOUS_CONTROLS | /*B_NOT_RESIZABLE*/ B_NOT_ZOOMABLE, + B_CURRENT_WORKSPACE) +{ + BRect viewRect = Bounds(); + + // Single header, Font Size 14. + BFont headerFont(be_bold_font); + headerFont.SetSize(14.0f); + font_height fh; + headerFont.GetHeight(&fh); + if (headerFont.StringWidth(status.info.name) > Bounds().Width() - 10) { + ResizeBy(headerFont.StringWidth(status.info.name) - Bounds().Width() + 10, 0); + } + + viewRect = Bounds(); + viewRect.bottom = fh.ascent + fh.descent; + BStringView *strView = new BStringView(viewRect, "header", status.info.name, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); + strView->SetFont(&headerFont); + strView->SetAlignment(B_ALIGN_CENTER); + + viewRect.left = 5; + viewRect.top = 10; + viewRect.bottom = Bounds().bottom - 5; + BTextView *txtView = new BTextView(viewRect, "infoText", viewRect, B_FOLLOW_LEFT | B_FOLLOW_TOP); + txtView->MakeEditable(false); + + BString strTracker(status.info.trackerAddress); + strTracker << ":" << status.info.trackerPort; + + BString strPieceSize(""); + StringForFileSize(status.info.pieceSize, &strPieceSize); + + BString strTotalSize(""); + StringForFileSize(status.info.totalSize, &strTotalSize); + + BString strDownloaded(""); + StringForFileSize(status.downloaded, &strDownloaded); + + BString strUploaded(""); + StringForFileSize(status.uploaded, &strUploaded); + + BString info(""); + info << "Tracker: " << strTracker << "\n" + << "Announce: " << status.info.trackerAnnounce << "\n" + << "Piece Size: " << strPieceSize << "\n" + << "Pieces: " << status.info.pieceCount << "\n" + << "Total Size: " << strTotalSize << "\n" + << "\n" + << "Folder: " << status.folder << "\n" + << "Downloaded: " << strDownloaded << "\n" + << "Uploaded: " << strUploaded << "\n"; + txtView->SetText(info.String()); + + Lock(); + AddChild(strView); + AddChild(txtView); + Unlock(); +} + +TRInfoWindow::~TRInfoWindow() { + +} + +void TRInfoWindow::FrameResized(float width, float height) { +} + +void TRInfoWindow::StringForFileSize(uint64_t size, BString *str) { + char *s = (char*)calloc(512, sizeof(char)); + if (size < 1024) { + sprintf(s, "%lld bytes", size); + } else if (size < 1048576) { + sprintf(s, "%lld.%lld KB", size / 1024, (size % 1024 ) / 103); + } else if (size < 1073741824 ) { + sprintf(s, "%lld.%lld MB", size / 1048576, (size % 1048576) / 104858); + } else { + sprintf(s, "%lld.%lld GB", size / 1073741824, (size % 1073741824) / 107374183); + } + + str->SetTo(s); + free(s); +} \ No newline at end of file diff --git a/beos/TRInfoWindow.h b/beos/TRInfoWindow.h new file mode 100644 index 000000000..2f0185aa1 --- /dev/null +++ b/beos/TRInfoWindow.h @@ -0,0 +1,22 @@ +#ifndef TR_INFO_WIND +#define TR_INFO_WIND + +#include +#include +#include + +#include "transmission.h" + +class TRInfoWindow : public BWindow { +public: + TRInfoWindow(tr_stat_t status); + ~TRInfoWindow(); + + virtual void FrameResized(float width, float height); +private: + void StringForFileSize(uint64_t size, BString *str); + + BBox *fBox; +}; + +#endif diff --git a/beos/TRPrefsWindow.cpp b/beos/TRPrefsWindow.cpp new file mode 100644 index 000000000..1ab03eabe --- /dev/null +++ b/beos/TRPrefsWindow.cpp @@ -0,0 +1,165 @@ +#include "TRPrefsWindow.h" +#include "Prefs.h" + +#include +#include +#include +#include + +#include +#include + +#include "TRApplication.h" + +TRPrefsWindow::TRPrefsWindow() : BWindow(BRect(0, 0, 300, 115), "Settings", + B_TITLED_WINDOW, + B_ASYNCHRONOUS_CONTROLS | B_NOT_CLOSABLE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE, + B_CURRENT_WORKSPACE) +{ + BRect viewRect = Bounds(); + viewRect.InsetBy(-1, -1); + BBox *box = new BBox(viewRect, NULL, B_FOLLOW_ALL); + + font_height fh; + be_plain_font->GetHeight(&fh); + + // Text Controls. + viewRect.Set(5, 5, viewRect.Width() - 10, fh.leading + fh.ascent + 10); + txtFolder = new BTextControl(viewRect, "txtFolder", "Download directory:", "", + NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP | B_FOLLOW_RIGHT); + box->AddChild(txtFolder); + + viewRect.Set(viewRect.left, viewRect.bottom + 10, + viewRect.right, viewRect.bottom + fh.leading + fh.ascent + 15); + txtPort = new BTextControl(viewRect, "txtPort", "Listen On Port:", "", + NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP | B_FOLLOW_RIGHT); + box->AddChild(txtPort); + + viewRect.Set(viewRect.left, viewRect.bottom + 10, + viewRect.right, viewRect.bottom + fh.leading + fh.ascent + 15); + txtUpload = new BTextControl(viewRect, "txtUpload", "Upload Limit (KB/sec):", "", + NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP | B_FOLLOW_RIGHT); + box->AddChild(txtUpload); + + // Buttons + viewRect.Set(viewRect.left, viewRect.bottom + 20, + viewRect.left + be_plain_font->StringWidth("Defaults") + 20, + viewRect.bottom + fh.leading + fh.ascent + 10); + btnDefaults = new BButton(viewRect, "btnDefault", "Defaults", new BMessage(TR_PREF_DEFAULTS)); + box->AddChild(btnDefaults); + + viewRect.OffsetBy(viewRect.Width() + 10, 0); + viewRect.right = viewRect.left + be_plain_font->StringWidth("Cancel") + 20; + btnCancel = new BButton(viewRect, "btnCancel", "Cancel", new BMessage(TR_PREF_CANCEL)); + box->AddChild(btnCancel); + + viewRect.OffsetBy(viewRect.Width() + 15, 0); + viewRect.right = viewRect.left + be_plain_font->StringWidth("Apply") + 20; + btnSave = new BButton(viewRect, "btnSave", "Apply", new BMessage(TR_PREF_SAVE)); + btnSave->MakeDefault(true); + box->AddChild(btnSave); + + Lock(); + AddChild(box); + ResizeTo(Bounds().Width(), viewRect.bottom + btnSave->Bounds().Height()); + + Unlock(); +} + + +TRPrefsWindow::~TRPrefsWindow() { +} + + +void TRPrefsWindow::MessageReceived(BMessage *msg) { + if (msg->what == TR_PREF_SAVE) { + if (WritePrefs()) { + Hide(); + } else { + beep(); + } + } else if (msg->what == TR_PREF_CANCEL) { + Hide(); + } else if (msg->what == TR_PREF_DEFAULTS) { + txtFolder->SetText("/boot/home/Downloads"); + txtPort->SetText("9000"); + txtUpload->SetText("20"); + } + BWindow::MessageReceived(msg); +} + + +void TRPrefsWindow::Show() { + ReadPrefs(); + if (Lock()) { + txtFolder->MakeFocus(true); + Unlock(); + } + BWindow::Show(); +} + + +void TRPrefsWindow::ReadPrefs() { + if (Lock()) { + Prefs prefs(TRANSMISSION_SETTINGS); + BString str(""); + if (prefs.FindString("download.folder", &str) != B_OK) { + prefs.SetString("download.folder", "/boot/home/Downloads"); + str << "/boot/home/Downloads"; + } + txtFolder->SetText(str.String()); + + int32 port; + if (prefs.FindInt32("transmission.bindPort", &port) != B_OK) { + prefs.SetInt32("transmission.bindPort", 9000); + port = 9000; + } + str = ""; + str << port; + txtPort->SetText(str.String()); + + int32 upload; + if (prefs.FindInt32("transmission.uploadLimit", &upload) != B_OK) { + prefs.SetInt32("transmission.uploadLimit", 20); + upload = 20; + } + str = ""; + str << upload; + txtUpload->SetText(str.String()); + + Unlock(); + } +} + + +bool TRPrefsWindow::WritePrefs() { + bool valid = true; + + int port = atoi(txtPort->Text()); + if (port <= 0) { + valid = false; + txtPort->MakeFocus(true); + } + + const char* uploadStr = txtUpload->Text(); + int uploadLimit = atoi(txtUpload->Text()); + + for (uint i = 0; i < strlen(uploadStr) && valid; i++) { + if (!(uploadStr[i] >= '0' && uploadStr[i] <= '9')) { + valid = false; + txtUpload->MakeFocus(true); + } + } + + if (valid) { + Prefs prefs(TRANSMISSION_SETTINGS); + + prefs.SetInt32("transmission.bindPort", (int32)port); + prefs.SetString("download.folder", txtFolder->Text()); + prefs.SetInt32("transmission.uploadLimit", (int32)uploadLimit); + + be_app_messenger.SendMessage(new BMessage(TR_RELOAD_SETTINGS)); + } + + return valid; +} diff --git a/beos/TRPrefsWindow.h b/beos/TRPrefsWindow.h new file mode 100644 index 000000000..85b869645 --- /dev/null +++ b/beos/TRPrefsWindow.h @@ -0,0 +1,34 @@ +#ifndef TR_PREF_WIND +#define TR_PREF_WIND + +#include +#include +#include +#include + +#define TR_PREF_SAVE 'tSve' +#define TR_PREF_CANCEL 'tCan' +#define TR_PREF_DEFAULTS 'tDef' + +class TRPrefsWindow : public BWindow { +public: + TRPrefsWindow(); + ~TRPrefsWindow(); + + virtual void MessageReceived(BMessage *msg); + + virtual void Show(); +private: + void ReadPrefs(); + bool WritePrefs(); + + BTextControl *txtFolder; + BTextControl *txtPort; + BTextControl *txtUpload; + + BButton *btnSave; + BButton *btnCancel; + BButton *btnDefaults; +}; + +#endif /* TR_PREF_WIND */ \ No newline at end of file diff --git a/beos/TRTransfer.cpp b/beos/TRTransfer.cpp new file mode 100644 index 000000000..96ed47b24 --- /dev/null +++ b/beos/TRTransfer.cpp @@ -0,0 +1,235 @@ +#include "TRTransfer.h" + +#include + +#include +#include + +/** + * BListItem that renders Transfer status. + */ +TRTransfer::TRTransfer(const char *fullpath, node_ref node) : BListItem(0, false), cachedNodeRef(node) { + fBaselineOffset = 0.0f; + fLineSpacing = 0.0f; + + cachedPath = new BString(fullpath); + fStatusLock = new BLocker("Status Locker", true); + + fStatus = (tr_stat_t*)calloc(1, sizeof(tr_stat_t)); + + fBarColor.red = 50; + fBarColor.green = 150; + fBarColor.blue = 255; + fBarColor.alpha = 255; + + fTimeStr = (char*)calloc(78, sizeof(char)); + fTransStr = (char*)calloc(78, sizeof(char)); +} + + +TRTransfer::~TRTransfer() { + if (fStatusLock->Lock()) { + free(fStatus); + fStatusLock->Unlock(); + delete fStatusLock; + } + delete cachedPath; +} + + +void TRTransfer::Update(BView *owner, const BFont *font) { + BListItem::Update(owner, font); + SetHeight(BListItem::Height() * 4); + + font_height height; + font->GetHeight(&height); + + fBaselineOffset = height.leading + height.ascent; + fLineSpacing = height.descent; +} + + +/** + * Sets the transfer information to render. + * Returns a bool signaling the view is dirty after the update. + * + * The view is determined to be dirty if the transfer + * status, progress, eta or the "shade" (even or odd) + * position in the list changes from the previous state. + * If the tr_stat_t is in fact different then the new, full + * status is memcpy'd overtop the existing code. + * + * This is a thread-safe function, as all writing to the + * local fStatus requires a successful Lock on fStatusLock. + */ +bool TRTransfer::SetStatus(tr_stat_t *stat, bool shade) { + bool dirty = false; + if (fStatusLock->Lock()) { + if (fStatus->status != stat->status || + fStatus->progress != stat->progress || + fStatus->eta != stat->eta || + fShade != shade) + { + memcpy(fStatus, stat, sizeof(tr_stat_t)); + dirty = true; + } + fStatusLock->Unlock(); + fShade = shade; + } + return dirty; +} + + +/** + * Renders (Draws) the current status into the BListView. + */ +void TRTransfer::DrawItem(BView *owner, BRect frame, bool complete) { + rgb_color col; + col.red = 255; + col.green = 255; + col.blue = 255; + + owner->PushState(); + + // Draw the background... + if (IsSelected()) { + owner->SetLowColor(tint_color(col, B_DARKEN_2_TINT)); + } else if (fShade) { + owner->SetLowColor(tint_color(tint_color(col, B_DARKEN_1_TINT), B_LIGHTEN_2_TINT)); + } else { + owner->SetLowColor(col); + } + owner->FillRect(frame, B_SOLID_LOW); + + // Draw the informational text... + owner->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR)); + BPoint textLoc = frame.LeftTop(); + textLoc.y += (fBaselineOffset / 2); + textLoc += BPoint(2, fBaselineOffset); + + if (fStatus != NULL && fStatusLock->Lock()) { + owner->DrawString(fStatus->info.name, textLoc); + + if (fStatus->status & TR_STATUS_PAUSE ) { + sprintf(fTimeStr, "Paused (%.2f %%)", 100 * fStatus->progress); + } else if (fStatus->status & TR_STATUS_CHECK ) { + sprintf(fTimeStr, "Checking Existing Files (%.2f %%)", + 100 * fStatus->progress); + } else if (fStatus->status & TR_STATUS_DOWNLOAD) { + if (fStatus->eta < 0 ) { + sprintf(fTimeStr, "--:--:-- Remaining (%.2f %%Complete)", + 100 * fStatus->progress); + } else { + sprintf(fTimeStr, "%02d:%02d:%02d Remaining (%.2f %%Complete)", + fStatus->eta / 3600, (fStatus->eta / 60) % 60, + fStatus->eta % 60, 100 * fStatus->progress); + } + } else if (fStatus->status & TR_STATUS_SEED) { + sprintf(fTimeStr, "Seeding"); + } else if (fStatus->status & TR_STATUS_STOPPING) { + sprintf(fTimeStr, "Stopping..."); + } else { + fTimeStr[0] = '\0'; + } + + textLoc.x = frame.Width() - owner->StringWidth(fTimeStr) - 2; + owner->DrawString(fTimeStr, textLoc); + + if (fStatus->status & (TR_STATUS_DOWNLOAD | TR_STATUS_SEED | TR_STATUS_CHECK )) { + // Move to the left of the bottom line. + textLoc.Set(frame.left + 2, + frame.top + fBaselineOffset * 3 + (2 * fLineSpacing) + (fBaselineOffset / 2)); + sprintf(fTransStr, "DL: %.2f KB/s (from %i of %i peer%s)", + fStatus->rateDownload, fStatus->peersUploading, fStatus->peersTotal, + (fStatus->peersTotal == 1) ? "" : "s"); + owner->DrawString(fTransStr, textLoc); + + sprintf(fTransStr, "UL: %.2f KB/s", fStatus->rateUpload); + textLoc.x = frame.Width() - owner->StringWidth(fTransStr) - 2; + owner->DrawString(fTransStr, textLoc); + } + + /* + * Progress Bar - Mercilessly ripped from the Haiku source code, and + * modified to handle selection tinting, and list item position shading. + */ + // Custom setup (Transmission Added) + BRect rect(frame.left + 2, frame.top + fBaselineOffset + fLineSpacing + (fBaselineOffset / 2), + frame.Width() - 2, frame.top + fBaselineOffset * 2 + fLineSpacing + (fBaselineOffset / 2)); + + // First bevel + owner->SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR ), B_DARKEN_1_TINT)); + if (IsSelected()) { + owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); + } + owner->StrokeLine(BPoint(rect.left, rect.bottom), BPoint(rect.left, rect.top)); + owner->StrokeLine(BPoint(rect.right, rect.top)); + + owner->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_2_TINT)); + if (IsSelected()) { + owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); + } + owner->StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), BPoint(rect.right, rect.bottom)); + owner->StrokeLine(BPoint(rect.right, rect.top + 1.0f)); + + rect.InsetBy(1.0f, 1.0f); + + // Second bevel + owner->SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR ), B_DARKEN_4_TINT)); + if (IsSelected()) { + owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); + } + owner->StrokeLine(BPoint(rect.left, rect.bottom), BPoint(rect.left, rect.top)); + owner->StrokeLine(BPoint(rect.right, rect.top)); + + owner->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + if (IsSelected()) { + owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); + } + owner->StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), BPoint(rect.right, rect.bottom)); + owner->StrokeLine(BPoint(rect.right, rect.top + 1.0f)); + + rect.InsetBy(1.0f, 1.0f); + + // Filling + owner->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_MAX_TINT)); + if (IsSelected()) { + owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); + } + owner->FillRect(rect); + + if (fStatus->progress != 0.0f) { + rect.right = rect.left + (float)ceil(fStatus->progress * (rect.Width() - 4)), + + // Bevel + owner->SetHighColor(tint_color(fBarColor, B_LIGHTEN_2_TINT)); + if (IsSelected()) { + owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); + } + owner->StrokeLine(BPoint(rect.left, rect.bottom), BPoint(rect.left, rect.top)); + owner->StrokeLine(BPoint(rect.right, rect.top)); + + owner->SetHighColor(tint_color(fBarColor, B_DARKEN_2_TINT)); + if (IsSelected()) { + owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); + } + owner->StrokeLine(BPoint(rect.left, rect.bottom), BPoint(rect.right, rect.bottom)); + owner->StrokeLine(BPoint(rect.right, rect.top)); + + rect.InsetBy(1.0f, 1.0f); + + // Filling + owner->SetHighColor(fBarColor); + if (IsSelected()) { + owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); + } + owner->FillRect(rect); + } + + fStatusLock->Unlock(); + } else { + owner->DrawString("loading...", textLoc); + } + + owner->PopState(); +} diff --git a/beos/TRTransfer.h b/beos/TRTransfer.h new file mode 100644 index 000000000..74b2b82a1 --- /dev/null +++ b/beos/TRTransfer.h @@ -0,0 +1,56 @@ +#ifndef TR_TRANSFER +#define TR_TRANSFER + +#include +#include +#include +#include +#include + +#include "transmission.h" + +class TRTransfer : public BListItem { +public: // Construction and Controll methods. + TRTransfer(const char *fullpath, node_ref node); + ~TRTransfer(); + + inline node_ref GetCachedNodeRef() { return cachedNodeRef; }; + inline const char* GetCachedPath() { return cachedPath->String(); }; + + bool SetStatus(tr_stat_t *stat, bool shade); + +public: // BListItem + virtual void Update(BView *owner, const BFont *font); + virtual void DrawItem(BView *owner, BRect frame, bool complete = false); + +private: + /* + * Cached data. The items stored here are _NOT_ necessairly + * the torrent we'll be rendering. It's likely they will be, + * but NOT guaranteed. They are not used for anything relating + * to rendering. + * + * Specifically we needed a way to cache the node_ref and + * reverse-lookup the node from the string path in the + * transmission structs. This seemed the logical place to store + * that information, since it ends up in a BList(View). + */ + node_ref cachedNodeRef; + BString *cachedPath; + +private: // Private members used for rendering. + float fBaselineOffset; + float fLineSpacing; + + BLocker *fStatusLock; + tr_stat_t *fStatus; + + rgb_color fBarColor; + + char* fTimeStr; + char* fTransStr; + + bool fShade; +}; + +#endif /* TR_TRANSFER */ diff --git a/beos/TRWindow.cpp b/beos/TRWindow.cpp new file mode 100644 index 000000000..559e93de9 --- /dev/null +++ b/beos/TRWindow.cpp @@ -0,0 +1,457 @@ +#include "TRWindow.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Prefs.h" +#include "TRApplication.h" +#include "TRTransfer.h" +#include "TRInfoWindow.h" + +/** + * The Transmission Window! Yay! + */ +TRWindow::TRWindow() : BWindow(BRect(10, 40, 350, 110), "Transmission", B_TITLED_WINDOW, + B_ASYNCHRONOUS_CONTROLS , B_CURRENT_WORKSPACE) +{ + engine = NULL; + Prefs prefs(TRANSMISSION_SETTINGS); + + BRect *rectFrame = new BRect(); + if (prefs.FindRect("window.frame", rectFrame) == B_OK) { + MoveTo(rectFrame->LeftTop()); + ResizeTo(rectFrame->Width(), rectFrame->Height()); + } else { + rectFrame->Set(10, 40, 350, 110); + } + Lock(); + + BRect viewRect(0, 0, rectFrame->Width(), rectFrame->Height()); + + BMenuBar *menubar = new BMenuBar(viewRect, "MenuBar"); + BMenu *menu = new BMenu("File"); + menu->AddItem(new BMenuItem("Open", new BMessage(TR_OPEN), 'O', B_COMMAND_KEY)); + menu->FindItem(TR_OPEN)->SetTarget(be_app_messenger); // send OPEN to the be_app. + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q', B_COMMAND_KEY)); + menubar->AddItem(menu); + + menu = new BMenu("Torrent"); + menu->AddItem(new BMenuItem("Get Info", new BMessage(TR_INFO), 'I', B_COMMAND_KEY)); + menu->FindItem(TR_INFO)->SetEnabled(false); + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("Resume", new BMessage(TR_RESUME))); + menu->AddItem(new BMenuItem("Pause", new BMessage(TR_PAUSE))); + menu->AddItem(new BMenuItem("Remove", new BMessage(TR_REMOVE))); + menubar->AddItem(menu); + + menu = new BMenu("Tools"); + menu->AddItem(new BMenuItem("Settings", new BMessage(TR_SETTINGS))); + menu->FindItem(TR_SETTINGS)->SetTarget(be_app_messenger); + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("About Transmission", new BMessage(B_ABOUT_REQUESTED))); + menu->FindItem(B_ABOUT_REQUESTED)->SetTarget(be_app_messenger); + menubar->AddItem(menu); + + AddChild(menubar); + SetKeyMenuBar(menubar); + + // TODO: Tool Bar? (Well after everything is working based on Menus) + + // Setup the transfers ListView + viewRect.Set(2, menubar->Frame().bottom + 3, rectFrame->Width() - 2 - B_V_SCROLL_BAR_WIDTH, rectFrame->Height() - 2); + transfers = new BListView(viewRect, "TorrentList", B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL); + transfers->SetSelectionMessage(new BMessage(TR_SELECT)); + AddChild(new BScrollView("TransferScroller", transfers, B_FOLLOW_ALL, 0, false, true)); + + Unlock(); + delete rectFrame; + + // Bring up the Transmission Engine + engine = tr_init(); + LoadSettings(); + + UpdateList(-1, true); + + // Start the message loop without showing the window. + Hide(); + Show(); +} + +TRWindow::~TRWindow() { + tr_close(engine); + stop_watching(this); +} + + +void TRWindow::LoadSettings() { + if (engine != NULL) { + Prefs prefs(TRANSMISSION_SETTINGS); + + int32 bindPort; + if (prefs.FindInt32("transmission.bindPort", &bindPort) != B_OK) { + bindPort = 9000; + prefs.SetInt32("transmission.bindPort", bindPort); + } + tr_setBindPort(engine, (int)bindPort); + + int32 uploadLimit; + if (prefs.FindInt32("transmission.uploadLimit", &uploadLimit) != B_OK) { + uploadLimit = 20; + prefs.SetInt32("transmission.uploadLimit", uploadLimit); + } + tr_setUploadLimit(engine, (int)uploadLimit); + } +} + + +/** + * Rescans the active Torrents folder, and will add all the torrents there to the + * engine. + */ +void TRWindow::RescanTorrents() { + if (Lock()) { + TRApplication *app = dynamic_cast(be_app); + BEntry *torrentEntry = new BEntry(); + status_t err; + + if (app->TorrentDir()->InitCheck() == B_OK) { + err = app->TorrentDir()->Rewind(); + while (err == B_OK) { + err = app->TorrentDir()->GetNextEntry(torrentEntry, true); + if (err != B_ENTRY_NOT_FOUND) { + AddEntry(torrentEntry); + } + } + } + delete torrentEntry; + Unlock(); + } +} + + +/** + * Adds the file specified by *torrent to the Transmission engine. + * Then adds a new TRTransfer item in the transfers list. + * This item holds cached information about the torrent entry and node. + * These TRTransmission items are _NOT_ guaranteed to render the entry + * they were created from. + */ +void TRWindow::AddEntry(BEntry *torrent) { + node_ref node; + if (torrent->GetNodeRef(&node) == B_OK) { + if (watch_node(&node, B_WATCH_NAME, this) == B_OK) { + BPath path; + torrent->GetPath(&path); + + // Try adding the torrent to the engine. + int addStatus = tr_torrentInit(engine, path.Path()); + if (addStatus == 0 && Lock()) { // Success. Add the TRTorrent item. + transfers->AddItem(new TRTransfer(path.Path(), node)); + + // Start the newly added torrent. + worker_info *startData = (worker_info*)calloc(1, sizeof(worker_info)); + startData->window = this; + startData->index = tr_torrentCount(engine) - 1; + thread_id start_thread = spawn_thread(TRWindow::AsynchStartTorrent, "BirthCanal", + B_NORMAL_PRIORITY, (void *)startData); + if (!((start_thread) < B_OK)) { + resume_thread(start_thread); + } else { // Fallback and start the old way. + StartTorrent(startData->index); + free(startData); + } + Unlock(); + } else { + bool duplicate = false; + TRTransfer* tr; + for (int32 i = 0; i < transfers->CountItems(); i++) { + tr = (TRTransfer*)transfers->ItemAt(i); + if (tr->GetCachedNodeRef() == node) { + duplicate = true; + } + } + if (!duplicate) { + BString errmsg("An error occurred trying to read "); + char namebuf[B_FILE_NAME_LENGTH]; + torrent->GetName(namebuf); + errmsg << namebuf; + errmsg << "."; + + BAlert *error = new BAlert("Error Opening Torrent", + errmsg.String(), + "Ok", NULL, NULL, + B_WIDTH_AS_USUAL, B_WARNING_ALERT); + error->Go(); + torrent->Remove(); + } + } + } + } +} + + +void TRWindow::MessageReceived(BMessage *msg) { + /* + * The only messages we receive from the node_monitor are if we need to + * stop watching the node. Basically, if it's been moved or removed we stop. + */ + if (msg->what == B_NODE_MONITOR) { + node_ref node; + ino_t fromDir; + ino_t toDir; + int32 opcode; + + if ((msg->FindInt32("opcode", &opcode) == B_OK) && + (msg->FindInt64("node", &node.node) == B_OK) && + (msg->FindInt32("device", &node.device) == B_OK)) + { + bool stop = (opcode == B_ENTRY_REMOVED); + + if (stop) { + msg->FindInt64("directory", &toDir); + } else { // It must have moved. + stop = ((msg->FindInt64("from directory", &fromDir) == B_OK) && + (msg->FindInt64("to directory", &toDir) == B_OK) && + (toDir != fromDir)); + } + + if (stop) { + watch_node(&node, B_STOP_WATCHING, this); + + /* Find the full path from the TRTorrents. + * The index of the TRTorrent that is caching the information + * IS NOT the index of the torrent in the engine. These are + * Totally decoupled, due to the way transmission is written. + */ + char path[B_FILE_NAME_LENGTH]; + TRTransfer* item; + for (int32 i = 0; i < transfers->CountItems(); i++) { + item = (TRTransfer*)transfers->ItemAt(i); + if (item->GetCachedNodeRef() == node) { + strcpy(path, item->GetCachedPath()); + } + } + + // Look for the torrent info in the engine with the matching + // path name. + tr_stat_t *stats; + tr_torrentStat(engine, &stats); + int index; + for (index = 0; index < tr_torrentCount(engine); index++) { + if (strcmp(stats[index].info.torrent, path) == 0) { + tr_torrentClose(engine, index); + transfers->RemoveItem(index); + break; + } + } + free(stats); + } + } + } else if (msg->what == TR_INFO) { + // Display an Info Window. + tr_stat_t *s; + tr_torrentStat(engine, &s); + + TRInfoWindow *info = new TRInfoWindow(s[transfers->CurrentSelection()]); + info->MoveTo(Frame().LeftTop() + BPoint(20, 25)); + info->Show(); + } else if (msg->what == TR_SELECT) { + // Setup the Torrent Menu enabled / disabled state. + int32 selection; + msg->FindInt32("index", &selection); + UpdateList(selection, true); + } else if (msg->what == TR_RESUME) { + worker_info *startData = (worker_info*)calloc(1, sizeof(worker_info)); + startData->window = this; + startData->index = (int)transfers->CurrentSelection(); + thread_id start_thread = spawn_thread(TRWindow::AsynchStartTorrent, "BirthCanal", + B_NORMAL_PRIORITY, (void *)startData); + if (!((start_thread) < B_OK)) { + resume_thread(start_thread); + } else { // Fallback and start the old way. + StartTorrent(startData->index); + free(startData); + } + } else if (msg->what == TR_PAUSE) { + worker_info *stopData = (worker_info*)calloc(1, sizeof(worker_info)); + stopData->window = this; + stopData->index = (int)transfers->CurrentSelection(); + thread_id stop_thread = spawn_thread(TRWindow::AsynchStopTorrent, "InUtero", + B_NORMAL_PRIORITY, (void *)stopData); + if (!((stop_thread) < B_OK)) { + resume_thread(stop_thread); + } else { // Fallback and stop it the old way. + StopTorrent(stopData->index); + free(stopData); + } + } else if (msg->what == TR_REMOVE) { + int32 index = transfers->CurrentSelection(); + + tr_torrentClose(engine, (int)index); + + // Remove the file from the filesystem. + TRTransfer *item = (TRTransfer*)transfers->RemoveItem(index); + BEntry *entry = new BEntry(item->GetCachedPath(), true); + entry->Remove(); + delete entry; + delete item; + + UpdateList(transfers->CurrentSelection(), true); + } else if (msg->what == B_SIMPLE_DATA) { + be_app->RefsReceived(msg); + } + + BWindow::MessageReceived(msg); +} + + +bool TRWindow::QuitRequested() { + bool quit = false; + + bool running = false; + tr_stat_t *s; + int max = tr_torrentStat(engine, &s); + for (int i = 0; i < max && !running; i++) { + running = (s[i].status & + (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED)); + } + free(s); + + if (running) { + BString quitMsg(""); + quitMsg << "There's " << max << " torrent"; + if (max > 1) { + quitMsg << "s"; + } + quitMsg << " currently running.\n" + << "What would you like to do?"; + + BAlert *confirmQuit = new BAlert("Confirm Quit", quitMsg.String(), + "Cancel", "Quit", NULL, + B_WIDTH_AS_USUAL, B_WARNING_ALERT); + quit = (confirmQuit->Go() == 1); + } else { + quit = true; + } + + if (quit) { + Prefs *prefs = new Prefs(TRANSMISSION_SETTINGS); + prefs->SetRect("window.frame", Frame()); + delete prefs; + + for (int i = 0; i < tr_torrentCount(engine); i++) { + tr_torrentStop(engine, i); + } + + be_app->PostMessage(new BMessage(B_QUIT_REQUESTED)); + } + return quit; +} + +void TRWindow::FrameResized(float width, float height) { + transfers->Invalidate(); +} + + +/** + * Called from the StopTorrent thread. + */ +void TRWindow::StopTorrent(int index) { + tr_torrentStop(engine, index); + + UpdateList(index, true); +} + +/** + * Called from StartTorrent thread. + */ +void TRWindow::StartTorrent(int index) { + // Read the settings. + BString folder(""); + Prefs *prefs = new Prefs(TRANSMISSION_SETTINGS); + if (prefs->FindString("download.folder", &folder) != B_OK) { + prefs->SetString("download.folder", "/boot/home/Downloads"); + folder << "/boot/home/Downloads"; + } + tr_torrentSetFolder(engine, index, folder.String()); + tr_torrentStart(engine, index); + + if (transfers->CurrentSelection() >= 0) { + UpdateList(index, true); + } + + delete prefs; +} + +/** + * Called from the be_app Pulse(); + * This will update the data structures that the TRTorrents use to render, + * and invalidate the view. + */ +void TRWindow::UpdateList(int32 selection = -1, bool menus = true) { + bool running = false; + + tr_stat_t * s; + int i = 0; + int max = tr_torrentStat(engine, &s); + bool invalid[max]; + + for (i = 0; i < max; i++) { + invalid[i] = ((TRTransfer*)transfers->ItemAt(i))->SetStatus(&(s[i]), (i % 2 != 0)); + + if (menus && i == (int)selection) { + running = (s[selection].status & + (TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED)); + } + } + free(s); + + if (menus) { + KeyMenuBar()->FindItem(TR_INFO)->SetEnabled(selection >= 0); + KeyMenuBar()->FindItem(TR_RESUME)->SetEnabled(selection >= 0 && !running); + KeyMenuBar()->FindItem(TR_PAUSE)->SetEnabled(selection >= 0 && running); + KeyMenuBar()->FindItem(TR_REMOVE)->SetEnabled(selection >= 0 && !running); + } + + if (Lock()) { + for (i = 0; i < max; i++) { + if (invalid[i]) { + transfers->InvalidateItem(i); + } + } + Unlock(); + } +} + +/** + * Thread Function to stop Torrents. This can be expensive and causes the event loop to + * choke. + */ +int32 TRWindow::AsynchStopTorrent(void *data) { + worker_info* stopData = (worker_info*)data; + stopData->window->StopTorrent(stopData->index); + free(stopData); + return B_OK; +} + +/** + * Thread Function to start Torrents. This can be expensive and causes the event loop to + * choke. + */ +int32 TRWindow::AsynchStartTorrent(void *data) { + worker_info* startData = (worker_info*)data; + startData->window->StartTorrent(startData->index); + free(startData); + return B_OK; +} \ No newline at end of file diff --git a/beos/TRWindow.h b/beos/TRWindow.h new file mode 100644 index 000000000..ca3844d0c --- /dev/null +++ b/beos/TRWindow.h @@ -0,0 +1,67 @@ +#ifndef TR_WIND +#define TR_WIND + +#include +#include +#include +#include + +#include "transmission.h" + +#include "TRPrefsWindow.h" + +#define TR_INFO 'tNfo' + +#define TR_RESUME 'tRes' +#define TR_PAUSE 'tPse' +#define TR_REMOVE 'tRmv' +#define TR_SELECT 'tSel' +#define TR_SETTINGS 'tSet' + + + +/** + * Transmission Window. + */ +class TRWindow : public BWindow { +public: // BWindow + TRWindow(); + ~TRWindow(); + + virtual void MessageReceived(BMessage *msg); + virtual bool QuitRequested(); + virtual void FrameResized(float width, float height); + +public: // TRWindow + void AddEntry(BEntry *torrent); + + void UpdateList(int32 selection, bool menus); + + void LoadSettings(); + + void StopTorrent(int index); + void StartTorrent(int index); + + static int32 AsynchStopTorrent(void *data); + static int32 AsynchStartTorrent(void *data); + + void RescanTorrents(); + +private: + BListView *transfers; + BFilePanel *openPanel; + + tr_handle_t *engine; + + TRPrefsWindow *fSettings; +}; + +/** + * Used to pass info off to the worker thread that runs AsynchStopTorrent + */ +struct worker_info { + TRWindow *window; + int index; +}; + +#endif /* TR_WIND */ diff --git a/beos/Transmission.rsrc b/beos/Transmission.rsrc new file mode 100644 index 0000000000000000000000000000000000000000..17b853aaaa55115c72da732a08fe472aec0b57c3 GIT binary patch literal 5628 zcmeHLJ#QRE7=HIQYa5a%wgZZjyTS-45DF+jWhpsq$F@$2V=HGxqVk5|8!RPGWSvY>Gw;_vvuo|0 zHW5v4+?>MYCjTaI`Uj2I64w46<~F_z@;*-=q4E5y=>PHt(e=XD(@A(ZllyskcA$R& zeQ>xvoyMu`&k)`4vqXQO|8CL0FiZ4PEg$?0`-MqA@hWp+2V?tr-N$tp*I}%Iu?EH( z7}fySBcH*U2ge&J-;zX)Bowf*_HZO2#}93mIW>Yn3Wn98$ugsz(j3cB2xJniRN}J8 z$#ce})(jaTV-jvzlsTtm94AJ;IbejWHJ}`3CUVx*>%yp%(-DOivJMP=sFhKPuGdv! z;6Vvv@<7O;S)EM4f0j*57-LhDNF8Mff<^6mJ;s&RRvDvbm9frfWjR?50%eRFr6+4) zI0gSQ1w}chgy3?J>-92cL>6)rqSH|ZrmT@dh=wWTmu z4=0gW8f9UIP{dsB03I@qJ>&v*u-;~2q?8e2uI?*Ik?Unqn7K$JZY4DQNufj>*EvC? zXe}7l*#tF4F}2O~LwMr!d?UxjciM!Jv5Dm3AvaasNP=@~k#tQVJ>;NKq5fUu0C5wn z%FcA^Qe&y6i`8QIcdiFG1#Z$*BL}D?_nf;22=(bhtTfsHG%R3OAd!Id^p+qi=}BE={e zN#f4LSlt;4t)B)55!d6n6JQv~TYtr|UM}ck)Bl}`(UqydyoKwa3X_%p*2rn z0LERmikje6E~^e;4H0GhRA?TzL2;8+W}J?(Cy#jn+=Pnx*`bYr)`o2`+@zi}%4&hV z;WWbNd+{*VXV!#PU~^&bG7g^{A7FpLfDIw!%p}ktPYuCMJn8TV#O}o&M}&aWG(9>> zsWK2*dsa#xKckWWdx9@*j0{t&f%`(IQU)b~ZJ_qpB{LnNeIKh~pVza6lo^=e#}_*F z_b@I|Pms)zq&qmX;XyPfcm_)A;eoa6fL+9r8Aypw`Q+dk1ZKeSNZ4Bd<3!jKIIK~| zb4R)55x&bfPJv3wzi>rO(JIG&8(<$46NT)Hw;|N#Wo`?dW9(&ZN?qf9$TIHhIG1f- zw@k3%nM$X4yFiwB=N7tzMZE3|{u7XRX>t?^gJEap2V^He!zzjKs`plLybO{?>*^n;!8ERhY87qvAJck;M#&tJk z?8C6ZLD9K5p25Cz=VO?~3UaSYr^UHBB9|MOnF^mjm$fPbnwxz-oDV)Wl-~0tL!;9M z*OoR`vE6Qz-z|c(cedKg^HKMk$2$j?QKx&4A3xZ=f3LgyXz#oG!szwBjkerac;J-<|iGk^cV zu8-p)8d-fy# zf=_~XYR{hd4|KOTmRmcJ27l3;rlpmQ_QI`&_SVhq*7C+}FBzuIm&l2mqug@0ms{&> zFYMb}AGXII0}FfW3x8{GZLPh|vGG1C;#+OqTG`(EY;&buJpVa(8ockqu4aH`AxwUAfb4A=5Jc)q=*Kb1(F1tG5={`tIIWzy1lF{sLhMdJX^p literal 0 HcmV?d00001 diff --git a/beos/libPrefs/Prefs.h b/beos/libPrefs/Prefs.h new file mode 100644 index 000000000..996bea2ba --- /dev/null +++ b/beos/libPrefs/Prefs.h @@ -0,0 +1,33 @@ +#ifndef LIBPREFS +#define LIBPREFS + +#include +#include +#include +#include + +class Prefs: public BMessage { + public: + Prefs(const char *fileName); + ~Prefs(); + inline status_t InitCheck(void) { return status; }; + void Save(); + + status_t SetBool(const char *name, bool b); + status_t SetInt8(const char *name, int8 i); + status_t SetInt16(const char *name, int16 i); + status_t SetInt32(const char *name, int32 i); + status_t SetInt64(const char *name, int64 i); + status_t SetFloat(const char *name, float f); + status_t SetDouble(const char *name, double d); + status_t SetString(const char *name, const char *string); + status_t SetPoint(const char *name, BPoint p); + status_t SetRect(const char *name, BRect r); + status_t SetMessage(const char *name, const BMessage *message); + status_t SetFlat(const char *name, const BFlattenable *obj); + + private: + BPath path; + status_t status; +}; +#endif diff --git a/beos/libPrefs/libPrefs.a b/beos/libPrefs/libPrefs.a new file mode 100644 index 0000000000000000000000000000000000000000..852317e7b47f3922ac5c8dcbb826eed187db84ed GIT binary patch literal 11116 zcmeHNeQaFC5g*%^RliVMl_FHp57D$LG%7BNfIn!33jT+pinc+vkOP!PYE<_(`*!d4 zt?yE|D*t7rceB5l*_nB}^Y-oAy^XiU2D{^TthzbiF6CvnRBo%N+_7_KAW$Ym`fngm zxuZfh=v+lar9{OW{-0Mgx;-*z_YOs>yD2)7wj4y!Xu4NbEvZ7ycDgnZOKVLc)#Vi- z=B4dqtSe!&%07E&D0aklY3&S2E-N(I-MWps?IGr{B$Db&x5;riO5q9UY* zQ;MjBa){_ApTn;dWD64LVyWqn*73h&=Hl&b&8Hb3{NhU`An{&%Ff#FwxBc|Fg@uKQ z42#C=Y9{a7(`^^*O*J#^ojvK2(PZ5o zCu){GFYNQZ$=mKHCjb_Hd-CU-S){jjoZ62aJRBXL>v(AjeP?vs#6!M`VSiiuMA)nR z_`Y;9y|xXev%J~W{Y9F}r2UaJn8)6*4?UY533L@(n+W?OU^~IiUc^uWm#xg4KW}(? zERMw+XO_=&%+J(%W;#BasrAluoSmul&2*fb49~-n7Ww8WunH(ga(a{YyILP*=DbUc zV-uT(Z^QxLXWhQ968T_g=sxE(pQgin{aEVmN58P2w=*Y4Xo+KU@xpNF!mvL#I;$N< zzVTT~jDD9f-}uMCOS0vkc&OBQ2D5qRjP<&9BqKkYS^n(W_BIzImqp_rFY&&Ynv>=| zi|0j8F(=6NYk%{yd-N}r&m#-;LD!z0%$&zGKkWN**#BsFVevzB+1eMw3vi*HqIJqX zv$H)sFH>sC{jd&`Azb!qme(p))~YLJbMBoSKF$xkm}}xim^HOI%aff@{g^V>_fu}t zM8C~?k*A_vf3Y*w&Q?t6^OrF>*jRJEcGsZoFW4$>aXZL!xZc4P#PuYuX?d-OJb>$42;o&+@8J3qu5-9(IT7Bk zqF`YsN|7CR=!0IL$Ma}W4G9q+xxFMl?0y=L&_n4+zb8JO$%p1(CyvOn-Eq`3;z6+gBWb}|rcUf?3c`&mAx?PsP zQ|bC)^9|_k*L1V7Ix#dUUB0I+ShwW8FX*;erH!~X5pNv2qtGpMZ_Sp!LD_u*y3^48 zY`z!{S-t~S>3(JRBy z--Z4Lb79+oNf+2JM99)Oup+7+GCILu4IE z4qJ)7E6TiOTGVhe6@o|d_3tWaZ3<4AXR)E@1&2hvg8 zFg~)JHjB7+JlZ=LJ1XTl)SWtd)J~?kW0zJw4K<`FPfw!k#_xy%sN+!*Wg~u9$~u$R zF;Shs*j|Dx`v7&er}||S_9DQlD0?0#>Gs&z6qMK= zKcmXt70}b|oq|1|GA{K%=vu?xR3K+>jnw1&u8{Un73B>CuikCUU|(Fg?=?3x4?TYO zsiLY()tIrYel2nnW3mo)exLGv_;K;Q>h=y@N;HUjQAOEPSW%ttp)(7L&(!^u%~Z{A~QyVI;ViFdly24@Y za9BIA-y1$q9gei@ZET3P1Vi=VXcWBKrr^GCG!(Ae+aPc{KA5@>eCLx^Y`oXiH->`s z(Z<@^=5UK_3XSX{ur=Y{oZlXb#L{uL5vuLOim{mKf-AHH$yl;tcQ`p^LMVBaBAO1msKCh#B5))*ZS+jmy(4q^R%bf!yI87(a9Hrb z2QH;7zfUQ2J5{Ge@R~p#kwTY`UiOhkP~i9sF65b7eiu^caxuy-@(2nX#yKwdgsz|q7P?#{ zq>Dv@{6~9^3qJ2F=z@hV7ems8DJXDI=(xb?VL=a&xg6kh(dj2cP~g-N4xE$7{0PKx&6^fPC7gx^z3JM)Z22h-`k=ujUTh}tlweeqt# zm&&Jz@QF(veDGWcT5`FwH{e-MbUX8}aK39j*r@1ajE~49(<_M{V2Tf;$C<*&Q%vzO z_$<=^5$_N)=6y@1uYt0^HyH<<^FGEOGTw;qb*#A=^M0gf6V@{lZ)JQ9(OnWBWQ-52 z6jN+?e1YjrMBk9|A2G&8>Swx}XiUn##yCXu zG}CIL=b6?J{Q;EQ|BZ1i_N$rJp+B(D^6bHO94O0gXM7jpW_maFE1A|4eO}5RW86UW zJ*JIBzhW98nvwGN88>09u_pC26J5@<1@VK5_!+kmbuewmcr!hK(-A3uk}>k)hfI+N zKVgbIm}ZJiOI{1}c+W89aRz0(A4=@ST9ff=P#)JCC9VSH{JWF+_Ym!4x)J9FOgG`E zoasSa$d{sPowaAtb?_ zf+BSVMT!cFR1_2`Cn!=&(A|>ONGkkdA0{AiT+%5?e=X^0NlTIU9LH`+6Ota6bXwAR zNjE^3_4i1cmh}6QzQzF`-+Rb?w!vozT<5}vH=k7f zHRJ)02it!Z^#D7R<&*TwE-u)2I{HFCT xB-vwyYzzNy+?wj@7yl65w|5_2>x9=YcJo5v#^zlu_e8?k|4_WU>Wr78{5J=yO~3#E literal 0 HcmV?d00001 diff --git a/beos/makefile b/beos/makefile new file mode 100644 index 000000000..6759e5f59 --- /dev/null +++ b/beos/makefile @@ -0,0 +1,107 @@ +## BeOS Generic Makefile v2.0 ## + +## Fill in this file to specify the project being created, and the referenced +## makefile-engine will do all of the hard work for you. This handles both +## Intel and PowerPC builds of the BeOS. + +## Application Specific Settings --------------------------------------------- + +# specify the name of the binary +NAME=Transmission + +# specify the type of binary +# APP: Application +# SHARED: Shared library or add-on +# STATIC: Static library archive +# DRIVER: Kernel Driver +TYPE= APP + +# specify the source files to use +# full paths or paths relative to the makefile can be included +# all files, regardless of directory, will have their object +# files created in the common object directory. +# Note that this means this makefile will not work correctly +# if two source files with the same name (source.c or source.cpp) +# are included from different directories. Also note that spaces +# in folder names do not work well with this makefile. +SRCS= \ + TRApplication.cpp\ + TRWindow.cpp\ + TRTransfer.cpp\ + TRPrefsWindow.cpp\ + TRInfoWindow.cpp\ +# + +# specify the resource files to use +# full path or a relative path to the resource file can be used. +RSRCS= Transmission.rsrc + +# specify additional libraries to link against +# there are two acceptable forms of library specifications +# - if your library follows the naming pattern of: +# libXXX.so or libXXX.a you can simply specify XXX +# library: libbe.so entry: be +# +# - if your library does not follow the standard library +# naming scheme you need to specify the path to the library +# and it's name +# library: my_lib.a entry: my_lib.a or path/my_lib.a +ifeq ($(wildcard /boot/develop/headers/be/bone/arpa/inet.h),) + LIBS = be net tracker transmission Prefs +else + LIBS = be bind socket tracker transmission Prefs +endif + +# specify additional paths to directories following the standard +# libXXX.so or libXXX.a naming scheme. You can specify full paths +# or paths relative to the makefile. The paths included may not +# be recursive, so include all of the paths where libraries can +# be found. Directories where source files are found are +# automatically included. +LIBPATHS= ../libtransmission ./libPrefs + +# additional paths to look for system headers +# thes use the form: #include
+# source file directories are NOT auto-included here +SYSTEM_INCLUDE_PATHS = + +# additional paths to look for local headers +# thes use the form: #include "header" +# source file directories are automatically included +LOCAL_INCLUDE_PATHS = ../libtransmission ./libPrefs + +# specify the level of optimization that you desire +# NONE, SOME, FULL +OPTIMIZE= SOME + +# specify any preprocessor symbols to be defined. The symbols +# will be set to a value of 1. For example specify DEBUG if you want +# DEBUG=1 to be set when compiling. +DEFINES= SYS_BEOS + +# specify special warning levels +# if unspecified default warnings will be used +# NONE = supress all warnings +# ALL = enable all warnings +WARNINGS = ALL + +# specify whether image symbols will be created +# so that stack crawls in the debugger are meaningful +# if TRUE symbols will be created +SYMBOLS = TRUE + +# specify debug settings +# if TRUE will allow application to be run from +# a source-level debugger +DEBUGGER = FALSE + +# specify additional compiler flags for all files +COMPILER_FLAGS = -fno-defer-pop + +# specify additional linker flags +LINKER_FLAGS = + +all: default + +## include the makefile-engine +include /boot/develop/etc/makefile-engine diff --git a/configure b/configure index 0bdde054d..51ef287c3 100755 --- a/configure +++ b/configure @@ -124,6 +124,18 @@ case $SYSTEM in BeOS) DEFINES="$DEFINES SYS_BEOS" + CC="gcc" + MACHINE=`uname -m` + case $MACHINE in + BePC) # BeOS on x86 + CPU="x86" + ;; + *) + CPU="ppc" + ;; + esac + SYSTEM="$SYSTEM / $CPU" + RELEASE=`uname -r` case $RELEASE in 6.0|5.0.4) # Zeta or R5 / BONE beta 7 @@ -137,7 +149,8 @@ case $SYSTEM in ;; *) echo "Unsupported BeOS version" - exit 1 ;; + exit 1 + ;; esac ;; @@ -205,6 +218,11 @@ HDRS += $OPENSSL_PREFIX/include ; LINKFLAGS += -L$OPENSSL_PREFIX/lib ; EOF fi +if [ -n "$CPU" ]; then +cat >> config.jam << EOF +CPU = $CPU ; +EOF +fi echo echo "To build Transmission, run 'jam'." diff --git a/libtransmission/Jamfile b/libtransmission/Jamfile index fa3fbcbbf..bed221edd 100644 --- a/libtransmission/Jamfile +++ b/libtransmission/Jamfile @@ -3,7 +3,7 @@ SubDir TOP libtransmission ; LIBTRANSMISSION_SRC = transmission.c bencode.c net.c tracker.c peer.c inout.c metainfo.c sha1.c utils.c upload.c fdlimit.c clients.c - completion.c ; + completion.c platform.c ; Library libtransmission.a : $(LIBTRANSMISSION_SRC) ; ObjectDefines $(LIBTRANSMISSION_SRC) : __TRANSMISSION__ ; diff --git a/libtransmission/platform.c b/libtransmission/platform.c new file mode 100644 index 000000000..0b22e45b5 --- /dev/null +++ b/libtransmission/platform.c @@ -0,0 +1,59 @@ +/****************************************************************************** + * Copyright (c) 2005 Eric Petit + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "platform.h" + +#ifdef SYS_BEOS +/*********************************************************************** + * tr_init_beos + *********************************************************************** + * Puts the prefsDirectory in the right place. + **********************************************************************/ +void tr_init_beos( tr_handle_t * h ) +{ + int32 length = 0; + char path[B_FILE_NAME_LENGTH]; + + find_directory( B_USER_SETTINGS_DIRECTORY, dev_for_path("/boot"), + true, path, length ); + + snprintf( h->prefsDirectory, B_FILE_NAME_LENGTH, + "%s/Transmission", path ); + mkdir( h->prefsDirectory, 0755 ); +} +#endif + +/*********************************************************************** + * tr_init_platform + *********************************************************************** + * Setup the prefsDirectory for the current platform. + **********************************************************************/ +void tr_init_platform( tr_handle_t *h ) +{ +#ifdef SYS_BEOS + tr_init_beos( h ); +#else + snprintf( h->prefsDirectory, sizeof( h->prefsDirectory ), + "%s/.transmission", getenv( "HOME" ) ); + mkdir( h->prefsDirectory, 0755 ); +#endif +} diff --git a/libtransmission/platform.h b/libtransmission/platform.h new file mode 100644 index 000000000..8ffee79f2 --- /dev/null +++ b/libtransmission/platform.h @@ -0,0 +1,42 @@ +#ifndef TR_PLATFORM_H +#define TR_PLATFORM_H 1 +/****************************************************************************** + * Copyright (c) 2005 Eric Petit + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "transmission.h" + + +#ifdef SYS_BEOS + #include + #include +#endif + +/*********************************************************************** + * tr_init_platform + *********************************************************************** + * Performs some platform specific initialization. + **********************************************************************/ +void tr_init_platform( tr_handle_t *h ); + + + +#endif \ No newline at end of file diff --git a/libtransmission/transmission.c b/libtransmission/transmission.c index d1df98a9a..ff5e16a4b 100644 --- a/libtransmission/transmission.c +++ b/libtransmission/transmission.c @@ -22,6 +22,12 @@ #include "transmission.h" +#ifdef __cplusplus +extern "C" { +#endif + +#include "platform.h" + /*********************************************************************** * Local prototypes **********************************************************************/ @@ -67,10 +73,8 @@ tr_handle_t * tr_init() h->bindPort = TR_DEFAULT_PORT; - snprintf( h->prefsDirectory, sizeof( h->prefsDirectory ), - "%s/.transmission", getenv( "HOME" ) ); - mkdir( h->prefsDirectory, 0755 ); - + tr_init_platform( h ); + return h; } @@ -488,6 +492,7 @@ static void downloadLoop( void * _tor ) tr_dbg( "Thread started" ); #ifdef SYS_BEOS + rename_thread(tor->thread, "torrent-tx"); /* This is required because on BeOS, SIGINT is sent to each thread, which kills them not nicely */ signal( SIGINT, SIG_IGN ); @@ -575,3 +580,7 @@ static float rateUpload( tr_torrent_t * tor ) { return rateGeneric( tor->dates, tor->uploaded ); } + +#ifdef __cplusplus +} +#endif diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index 521838719..31f649a18 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -23,10 +23,19 @@ #ifndef TR_TRANSMISSION_H #define TR_TRANSMISSION_H 1 +#ifdef __cplusplus +extern "C" { +#endif + #include #define SHA_DIGEST_LENGTH 20 -#define MAX_PATH_LENGTH 1024 +#ifdef SYS_BEOS +# include +# define MAX_PATH_LENGTH B_FILE_NAME_LENGTH +#else +# define MAX_PATH_LENGTH 1024 +#endif #define TR_MAX_TORRENT_COUNT 50 #define TR_DEFAULT_PORT 9090 @@ -234,4 +243,8 @@ struct tr_stat_s # include "internal.h" #endif +#ifdef __cplusplus +} +#endif + #endif