blob: 7593f4def81a6ab7eff1b4c28682445d219a849b [file] [log] [blame]
/*
* R : A Computer Language for Statistical Data Analysis
* file util.c
* Copyright (C) 2005--2019 The R Core Team
* Copyright (C) 1998--2003 Guido Masarotto and Brian Ripley
* Copyright (C) 2004 The R Foundation
*
* 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, a copy is available at
* https://www.R-project.org/Licenses/
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <Defn.h>
#include <windows.h>
#include "win-nls.h"
typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
// keep in step with src/gnuwin32/extra.c
SEXP winver(void)
{
char ver[256];
OSVERSIONINFOEX osvi;
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if(!GetVersionEx((OSVERSIONINFO *)&osvi))
error(_("unsupported version of Windows"));
/* see http://msdn2.microsoft.com/en-us/library/ms724429.aspx
for ways to get more info.
Pre-NT versions are all 4.x, so no need to separate test.
See also http://msdn.microsoft.com/en-us/library/ms724832.aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833%28v=vs.85%29.aspx
for version number naming.
*/
if(osvi.dwMajorVersion >= 5) {
char *desc = "", *type="";
SYSTEM_INFO si;
// future-proof
snprintf(ver, 256, "%d.%d",
(int) osvi.dwMajorVersion, (int) osvi.dwMinorVersion);
if(osvi.dwMajorVersion == 10) {
if(osvi.wProductType == VER_NT_WORKSTATION) desc = "10";
else desc = "Server";
} else if(osvi.dwMajorVersion == 6) {
// see See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451%28v=vs.85%29.aspx for the >= here.
if(osvi.wProductType == VER_NT_WORKSTATION) {
if(osvi.dwMinorVersion == 0) desc = "Vista";
else if(osvi.dwMinorVersion == 1) desc = "7";
else if(osvi.dwMinorVersion == 2) desc = ">= 8";
else if(osvi.dwMinorVersion == 3) desc = "8.1";
else desc = "> 8.1";
} else {
if(osvi.dwMinorVersion == 0) desc = "Server 2008";
else if(osvi.dwMinorVersion == 1) desc = "Server 2008 R2";
else if(osvi.dwMinorVersion == 2) desc = "Server >= 2012";
else if(osvi.dwMinorVersion == 3) desc = "Server 2012 R2";
else desc = "Server > 2012";
}
} else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
desc = "2000";
else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
desc = "XP";
else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
if(osvi.wProductType == VER_NT_WORKSTATION)
desc = "XP Professional";
else
desc = "Server 2003";
}
/* GetNativeSystemInfo is XP or later */
GetNativeSystemInfo(&si);
if(si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
type = " x64";
if(osvi.wServicePackMajor > 0)
snprintf(ver, 256,
"Windows %s%s (build %d) Service Pack %d",
desc, type,
LOWORD(osvi.dwBuildNumber),
(int) osvi.wServicePackMajor);
else
snprintf(ver, 256,
"Windows %s%s (build %d)",
desc, type,
LOWORD(osvi.dwBuildNumber));
} else { /* should not get here */
snprintf(ver, 256, "Windows %d.%d (build %d) %s",
(int) osvi.dwMajorVersion, (int) osvi.dwMinorVersion,
LOWORD(osvi.dwBuildNumber), osvi.szCSDVersion);
}
return mkString(ver);
}
SEXP dllversion(SEXP path)
{
const wchar_t *dll;
DWORD dwVerInfoSize;
DWORD dwVerHnd;
if(!isString(path) || LENGTH(path) != 1)
error(_("invalid '%s' argument"), "path");
dll = filenameToWchar(STRING_ELT(path, 0), FALSE);
dwVerInfoSize = GetFileVersionInfoSizeW(dll, &dwVerHnd);
SEXP ans = PROTECT(allocVector(STRSXP, 2));
SET_STRING_ELT(ans, 0, mkChar(""));
SET_STRING_ELT(ans, 1, mkChar(""));
if (dwVerInfoSize) {
BOOL fRet;
LPSTR lpstrVffInfo;
LPSTR lszVer = NULL;
UINT cchVer = 0;
lpstrVffInfo = (LPSTR) malloc(dwVerInfoSize);
if (GetFileVersionInfoW(dll, 0L, dwVerInfoSize, lpstrVffInfo)) {
fRet = VerQueryValue(lpstrVffInfo,
TEXT("\\StringFileInfo\\040904E4\\FileVersion"),
(LPVOID)&lszVer, &cchVer);
if(fRet) SET_STRING_ELT(ans, 0, mkChar(lszVer));
fRet = VerQueryValue(lpstrVffInfo,
TEXT("\\StringFileInfo\\040904E4\\R Version"),
(LPVOID)&lszVer, &cchVer);
if(fRet) SET_STRING_ELT(ans, 1, mkChar(lszVer));
else {
fRet = VerQueryValue(lpstrVffInfo,
TEXT("\\StringFileInfo\\040904E4\\Compiled under R Version"),
(LPVOID)&lszVer, &cchVer);
if(fRet) SET_STRING_ELT(ans, 1, mkChar(lszVer));
}
} else ans = R_NilValue;
free(lpstrVffInfo);
} else ans = R_NilValue;
UNPROTECT(1);
return ans;
}
SEXP getClipboardFormats(void)
{
SEXP ans = R_NilValue;
int j, size, format = 0;
if(OpenClipboard(NULL)) {
size = CountClipboardFormats();
PROTECT(ans = allocVector(INTSXP, size));
for (j = 0; j < size; j++) {
format = EnumClipboardFormats(format);
INTEGER(ans)[j] = format;
}
UNPROTECT(1);
CloseClipboard();
}
return ans;
}
#define STRICT_R_HEADERS
#include <R_ext/RS.h>
/* split on \r\n or just one */
static SEXP splitClipboardText(const char *s, int ienc)
{
int cnt_r= 0, cnt_n = 0, n, nc, nl, line_len = 0;
const char *p;
char *line, *q, eol = '\n';
Rboolean last = TRUE; /* does final line have EOL */
Rboolean CRLF = FALSE;
SEXP ans;
for(p = s, nc = 0; *p; p++, nc++)
switch(*p) {
case '\n':
cnt_n++;
last = TRUE;
line_len = max(line_len, nc);
nc = -1;
break;
case '\r':
cnt_r++;
last = TRUE;
break;
default:
last = FALSE;
}
if (!last) line_len = max(line_len, nc); /* the unterminated last might be the longest */
n = max(cnt_n, cnt_r) + (last ? 0 : 1);
if (cnt_n == 0 && cnt_r > 0) eol = '\r';
if (cnt_r == cnt_n) CRLF = TRUE;
/* over-allocate a line buffer */
line = R_chk_calloc(1+line_len, 1);
PROTECT(ans = allocVector(STRSXP, n));
for(p = s, q = line, nl = 0; *p; p++) {
if (*p == eol) {
*q = '\0';
SET_STRING_ELT(ans, nl++, mkCharCE(line, ienc));
q = line;
*q = '\0';
} else if(CRLF && *p == '\r')
;
else *q++ = *p;
}
if (!last) {
*q = '\0';
SET_STRING_ELT(ans, nl, mkCharCE(line, ienc));
}
R_chk_free(line);
UNPROTECT(1);
return(ans);
}
SEXP readClipboard(SEXP sformat, SEXP sraw)
{
SEXP ans = R_NilValue;
HGLOBAL hglb;
const char *pc;
int j, format, raw, size;
format = asInteger(sformat);
raw = asLogical(sraw);
if(OpenClipboard(NULL)) {
if(IsClipboardFormatAvailable(format) &&
(hglb = GetClipboardData(format)) &&
(pc = (const char *) GlobalLock(hglb))) {
if(raw) {
Rbyte *pans;
size = GlobalSize(hglb);
ans = allocVector(RAWSXP, size); /* no R allocation below */
pans = RAW(ans);
for (j = 0; j < size; j++) pans[j] = *pc++;
} else if (format == CF_UNICODETEXT) {
int n, ienc = CE_NATIVE;
const wchar_t *wpc = (wchar_t *) pc;
n = wcslen(wpc);
char text[4*n+1];
R_CheckStack();
wcstoutf8(text, wpc, sizeof(text));
if(!strIsASCII(text)) ienc = CE_UTF8;
ans = splitClipboardText(text, ienc);
} else if (format == CF_TEXT || format == CF_OEMTEXT || format == CF_DIF) {
/* can we get the encoding out of a CF_LOCALE entry? */
ans = splitClipboardText(pc, 0);
} else
error("'raw = FALSE' and format is a not a known text format");
GlobalUnlock(hglb);
}
CloseClipboard();
}
return ans;
}
SEXP writeClipboard(SEXP text, SEXP sformat)
{
int i, n, format;
HGLOBAL hglb;
char *s;
const char *p;
Rboolean success = FALSE, raw = FALSE;
const void *vmax = vmaxget();
format = asInteger(sformat);
if (TYPEOF(text) == RAWSXP) raw = TRUE;
else if(!isString(text))
error(_("argument must be a character vector or a raw vector"));
n = length(text);
if(n > 0) {
int len = 1;
if(raw) len = n;
else if (format == CF_UNICODETEXT)
for(i = 0; i < n; i++)
len += 2 * (wcslen(wtransChar(STRING_ELT(text, i))) + 2);
else
for(i = 0; i < n; i++)
len += strlen(translateChar(STRING_ELT(text, i))) + 2;
if ( (hglb = GlobalAlloc(GHND, len)) &&
(s = (char *)GlobalLock(hglb)) ) {
if(raw)
for(i = 0; i < n; i++) *s++ = RAW(text)[i];
else if (format == CF_UNICODETEXT) {
const wchar_t *wp;
wchar_t *ws = (wchar_t *) s;
for(i = 0; i < n; i++) {
wp = wtransChar(STRING_ELT(text, i));
while(*wp) *ws++ = *wp++;
*ws++ = L'\r'; *ws++ = L'\n';
}
*ws = L'\0';
} else {
for(i = 0; i < n; i++) {
p = translateChar(STRING_ELT(text, i));
while(*p) *s++ = *p++;
*s++ = '\r'; *s++ = '\n';
}
*s = '\0';
}
GlobalUnlock(hglb);
if (!OpenClipboard(NULL) || !EmptyClipboard()) {
warning(_("unable to open the clipboard"));
GlobalFree(hglb);
} else {
success = SetClipboardData(format, hglb) != 0;
if(!success) {
warning(_("unable to write to the clipboard"));
GlobalFree(hglb);
}
CloseClipboard();
}
}
}
vmaxset(vmax);
return ScalarLogical(success);
}
#include "Startup.h"
#include <graphapp/ga.h>
#include "rui.h"
SEXP getIdentification(void)
{
const char *res = "" /* -Wall */;
switch(CharacterMode) {
case RGui:
if(RguiMDI & RW_MDI) res = "RGui"; else res = "R Console";
break;
case RTerm:
res = "Rterm";
break;
default:
/* do nothing */
break; /* -Wall */
}
return mkString(res);
}
SEXP getWindowTitle(void)
{
char buf[512], *res = "";
switch(CharacterMode) {
case RGui:
if(RguiMDI & RW_MDI) res = GA_gettext(RFrame);
else res = GA_gettext(RConsole);
break;
case RTerm:
GetConsoleTitle(buf, 512);
buf[511] = '\0';
res = buf;
break;
default:
/* do nothing */
break;
}
return mkString(res);
}
static SEXP in_setTitle(const char *title)
{
SEXP result = getWindowTitle();
switch(CharacterMode) {
case RGui:
if(RguiMDI & RW_MDI) settext(RFrame, title);
else settext(RConsole, title);
break;
case RTerm:
SetConsoleTitle(title);
break;
default:
/* do nothing */
break; /* -Wall */
}
return result;
}
SEXP setWindowTitle(SEXP title)
{
if(!isString(title) || LENGTH(title) != 1 ||
STRING_ELT(title, 0) == NA_STRING)
error(_("'title' must be a character string"));
return in_setTitle(translateChar(STRING_ELT(title, 0)));
}
SEXP setStatusBar(SEXP text)
{
if(!isString(text) || LENGTH(text) != 1 ||
STRING_ELT(text, 0) == NA_STRING)
error(_("'text' must be a character string"));
showstatusbar();
setstatus(translateChar(STRING_ELT(text, 0)));
return R_NilValue;
}
static void * getConsoleHandle(const char *which)
{
if (CharacterMode != RGui) return(NULL);
else if (strcmp(which, "Console") == 0 && RConsole)
return getHandle(RConsole);
else if (strcmp(which, "Frame") == 0 && RFrame)
return getHandle(RFrame);
else if (strcmp(which, "Process") == 0)
return GetCurrentProcess();
else return NULL;
}
#include <R_ext/GraphicsEngine.h>
#include "devWindows.h"
static void *getDeviceHandle(int dev)
{
pGEDevDesc gdd;
gadesc *xd;
if (dev == -1) return(getHandle(RConsole));
if (dev < 1 || dev > R_MaxDevices || dev == NA_INTEGER) return(0);
gdd = GEgetDevice(dev - 1);
if (!gdd) return(NULL);
xd = (gadesc *) gdd->dev->deviceSpecific;
if (!xd) return(NULL);
return getHandle(xd->gawin);
}
SEXP getWindowsHandle(SEXP which)
{
void * handle;
if(length(which) != 1) error(_("'%s' must be length 1"), "which");
if (isString(which)) handle = getConsoleHandle(CHAR(STRING_ELT(which,0)));
else if (isInteger(which)) handle = getDeviceHandle(INTEGER(which)[0]);
else handle = NULL;
if (handle)
return R_MakeExternalPtr(handle,R_NilValue,R_NilValue);
else
return R_NilValue;
}
static SEXP EnumResult;
static int EnumCount;
static PROTECT_INDEX EnumIndex;
static int EnumMinimized;
static DWORD EnumProcessId;
static BOOL CALLBACK EnumWindowsProc(HWND handle, LPARAM param)
{
char title[1024];
if (IsWindowVisible(handle)) {
if (EnumProcessId) { /* restrict to R windows only */
DWORD processId;
GetWindowThreadProcessId(handle, &processId);
if (processId != EnumProcessId) return TRUE;
}
if (!EnumMinimized && IsIconic(handle)) return TRUE;
if (EnumCount >= length(EnumResult)) {
int newlen = 2*length(EnumResult);
REPROTECT(EnumResult = lengthgets(EnumResult, newlen), EnumIndex);
setAttrib(EnumResult, R_NamesSymbol,
lengthgets(getAttrib(EnumResult, R_NamesSymbol), newlen));
}
SET_VECTOR_ELT(EnumResult, EnumCount, R_MakeExternalPtr(handle,R_NilValue,R_NilValue));
if (GetWindowText(handle, title, 1024))
SET_STRING_ELT(getAttrib(EnumResult, R_NamesSymbol), EnumCount, mkChar(title));
EnumCount++;
}
return TRUE;
}
SEXP getWindowsHandles(SEXP which, SEXP minimized)
{
PROTECT_WITH_INDEX(EnumResult = allocVector(VECSXP, 8), &EnumIndex);
setAttrib(EnumResult, R_NamesSymbol, allocVector(STRSXP, 8));
EnumCount = 0;
const char * w;
w = CHAR(STRING_ELT(which, 0));
EnumMinimized = asLogical(minimized);
if (strcmp(w, "R") == 0) EnumProcessId = GetCurrentProcessId();
else EnumProcessId = 0;
if (ismdi() && EnumProcessId)
EnumChildWindows(GetParent(getHandle(RConsole)), EnumWindowsProc, 0);
else
EnumWindows(EnumWindowsProc, 0);
EnumResult = lengthgets(EnumResult, EnumCount);
UNPROTECT(1);
return EnumResult;
}
static void
in_ArrangeWindows(int n, void** windows, int action, int preserve, int outer)
{
int j;
if (action == MINIMIZE || action == RESTORE) {
for (j=0; j<n; j++)
ShowWindow((HWND)windows[j], action == MINIMIZE ? SW_MINIMIZE : SW_RESTORE);
} else {
RECT rect = {0,0,0,0};
RECT *prect = &rect;
HWND parent;
if (preserve) {
WINDOWPLACEMENT wp;
wp.length = sizeof(wp);
for (j=0; j<n; j++) {
if (GetWindowPlacement((HWND)windows[j], &wp)) {
UnionRect(prect, prect, &wp.rcNormalPosition);
if (wp.showCmd == SW_SHOWMINIMIZED || wp.showCmd == SW_SHOWMAXIMIZED) {
wp.showCmd = SW_RESTORE;
SetWindowPlacement((HWND)windows[j], &wp);
}
}
}
}
if (rect.left == rect.right || rect.top == rect.bottom) prect = NULL;
if (!outer && ismdi())
parent = GetParent(getHandle(RConsole));
else
parent = NULL;
switch (action) {
case CASCADE: CascadeWindows(parent, 0, prect, n, (HWND FAR *)windows);
break;
case TILEHORIZ: TileWindows(parent, MDITILE_HORIZONTAL, prect, n, (HWND FAR *)windows);
break;
case TILEVERT: TileWindows(parent, MDITILE_VERTICAL, prect, n, (HWND FAR *)windows);
break;
}
}
}
SEXP arrangeWindows(SEXP call, SEXP op, SEXP args, SEXP env)
{
SEXP windows;
int action, preserve, outer;
args = CDR(args);
windows = CAR(args);
if (length(windows)) {
if (TYPEOF(windows) != VECSXP) error(_("'%s' must be a list"), "windows");
void **handles = (void **) R_alloc(length(windows), sizeof(void *));
for (int i = 0; i < length(windows); i++) {
if (TYPEOF(VECTOR_ELT(windows, i)) != EXTPTRSXP)
error(_("'%s' element %d is not a window handle"), "windows", i+1);
handles[i] = R_ExternalPtrAddr(VECTOR_ELT(windows, i));
}
action = asInteger(CADR(args));
preserve = asInteger(CADDR(args));
outer = asInteger(CADDDR(args));
in_ArrangeWindows(length(windows), handles, action, preserve, outer);
}
return windows;
}